Jump to content

Recommended Posts

I have a class TProvider where I create 1-n instances in my application. Now I need a threadvar for each instance. The number of instances will never be more than up to 50 objects.

 

What is a good pattern to create a temporary data block per thread/object?

 

Regards
Christian

Share this post


Link to post

Threadvar(s) are invisible to anything else than this thread (current), and each thread have its copy.

 

So if you are going to use them from different thread like the main or whatever, you need to store them or store a reference to them somewhere and this somewhere will definitely need a thread locking or thread safe storage, thus this storage will negate all the trouble to use them in first place, use global list or something, by global i mean centralized list or container with adequate thread safety.

 

in other words

28 minutes ago, chkaufmann said:

What is a good pattern to create a temporary data block per thread/object?

You don't store data block per thread and per object at the same time, attach one and reference the other. 

Share this post


Link to post

This is my use case. 

  TBSItemProvider = class(TBSManagedInterface)
  strict private
    FNotificationCache  : Integer;                            
    FNotificationCaches : IBSList<TNotifyCacheItem>;          
  public 
    procedure DisableNotifications;
    procedure EnableNotifications;
   end;

My application has 1-n objects of type TBSItemProvider. Basically this is used to handle the records of one database table.

 

Normally the provider distributes messages/notifications after each insert/update/delete operation of one item.

 

When a thread changes many items, I call DisableNotifications and in this case, notifications are cached in FNotificationCaches. So this is the reason these two private variables should be "per thread".

 

Christian

Share this post


Link to post

Well, i still can't see how threadvar will help here.

 

I mean you wrote "I call DisableNotifications", how and from where ? see, again threadvar are invisible to other threads and to reach them you need this thread to get some value in like receiving signal (notified to update) or reading from somewhere fixed or predefined, or accessing a already known list, to get the needed value then store it in threadvar.

 

again in that case and after going through all this troubles to get these values, threadvar is not providing any thing useful, because you already implemented the data passage and sharing/signaling/locking to the specific thread.

 

As a solution then just have a list or an array with objects or records, example an object like your TBSItemProvider that is created by the main thread (the origin doesn't matter) and attached/passed to the thread, this will simplify the whole thread communication process. 

Of course you need to take care of thread synchronization in case you will do access them randomly, but again in case of threadvar will work for you then such a record or object will be the same.

Share this post


Link to post

The naive answer to your question is that the object holds a dictionary whose key is the thread id. But it's far from clear that what you are planning will actually solve your problem. 

  • Like 1

Share this post


Link to post
2 hours ago, chkaufmann said:

My application has 1-n objects of type TBSItemProvider

Is any instance of TBSItemProvider accessed by multiple threads?

Share this post


Link to post
3 hours ago, chkaufmann said:

So this is the reason these two private variables should be "per thread".

Those are fields in a class, threadvar is only supported for global variables.

 

There is not enough context around what you are trying to do, so it is hard to say what is the most appropriate solution. From how it looks now, I would say that what @David Heffernan proposed looks most suitable. You would have to create and remove items in the dictionary when thread is created and destroyed. 

 

Another question is, are you sharing other data within TBSItemProvider instance between threads which sounds like you are doing, and what kind of protection you have around that data. If it is not read only then sharing data is not thread-safe.

Share this post


Link to post
1 hour ago, Dalija Prasnikar said:

threadvar is only supported for global variables

and for class var.

  • Like 1

Share this post


Link to post
14 hours ago, Dalija Prasnikar said:

There is not enough context around what you are trying to do, so it is hard to say what is the most appropriate solution. From how it looks now, I would say that what @David Heffernan proposed looks most suitable. You would have to create and remove items in the dictionary when thread is created and destroyed. 

The TBSItemProvider objects are used by any thread. And what David writes will work. I was thinking of something like this as well. The question for me is, how to do the cleanup: Either I have to call a function at the end of each thread or is there a global place to do this?

 

Christian

Share this post


Link to post
27 minutes ago, chkaufmann said:

The TBSItemProvider objects are used by any thread. And what David writes will work. I was thinking of something like this as well. The question for me is, how to do the cleanup: Either I have to call a function at the end of each thread or is there a global place to do this?

You would need to call method at the beginning code that runs in a thread that would add that thread ID into dictionary and create cache instance for that key. At the end of the thread code you would also call method that would be responsible for removing that key and cache from the dictionary. This add/ remove logic should be protected by some locking mechanism contained within TBSItemProvider instance and protected with try...finally block so that cleanup is done in case code in between raises an exception. 

 

If you share other data within that instance between threads then any such access should also be protected by a lock, unless all threads are only reading the data.

Share this post


Link to post
3 hours ago, Dalija Prasnikar said:

If you share other data within that instance between threads then any such access should also be protected by a lock

Does anyone use TMultiReadExclusiveWriteSynchronizer? The application team I was on 20 years ago had some problems with it in D6. But I see that the VCL still uses it for GlobalNameSpace (for Windows anyway).

Share this post


Link to post
1 hour ago, JonRobertson said:

Does anyone use TMultiReadExclusiveWriteSynchronizer? The application team I was on 20 years ago had some problems with it in D6. But I see that the VCL still uses it for GlobalNameSpace (for Windows anyway).

Search broken again?

https://en.delphipraxis.net/search/?q=TMultiReadExclusiveWriteSynchronizer

https://www.google.com/search?q=TMultiReadExclusiveWriteSynchronizer

Share this post


Link to post
3 minutes ago, Anders Melander said:

Search broken again?

Nope. It returns results like

On 1/27/2019 at 4:33 AM, dummzeuch said:

It does use various instances of TMultiReadExclusiveWriteSynchronizer which, if I remember correctly, was broken in several RTL versions. So using that class might not have been the best decision.

Quote

it is terribly slow. So slow that it is actually almost never OK to use it. If a protected area of code is short, then it is almost always better to use a simple critical section for synchronization.

Quote

it appears there have been some major historical issues with TMultiReadExclusiveWriteSynchronizer but there’s no clear indication of whether this is still the case

Numerous posts about TMREWS having issue and asking if those were fixed. A few posts suggesting an alternate solution.

 

I trust members of Delphi-Praxis more than most other online resources. So I thought I'd ask. :classic_wink:

Share this post


Link to post

The suitability of TMultiReadExclusiveWriteSynchronizer depends on how you use it and what your needs are and only you know the answer to that. There's a more lightweight and faster alternative available in the RTL of recent Delphi versions. Google, or the help, will find the name for you.

 

TMultiReadExclusiveWriteSynchronizer got broken when Danny Thorpe rewrote it for D6. I believe it was fixed in D7. Before the rewrite it supported lock promotion which opened up for deadlocks in some usage patterns - not because it was buggy but because people didn't know what they were doing.

Share this post


Link to post
4 minutes ago, Anders Melander said:

There's a more lightweight and faster alternative available in the RTL of recent Delphi versions

TLightweightMREW

5 minutes ago, Anders Melander said:

because people didn't know what they were doing

Our "issue" at the time was RTL code. Specially in a DCOM based MIDAS/DataSnap server that created a thread for each user connection. Once the DataSnap server had over 100 users connected, it would frequently lock up due to a deadlock with GlobalnameSpace.

 

However, the contributing factor was we were doing "too much" in RemoteDataModuleCreate, which extended the amount of time that GlobalNameSpace was locked. We also had multiple TRemoteDataModules due to the size of our app.  While I don't disagree with your assertion of "people didn't know what they were doing", it wasn't because we were misusing TMREWS. We never used it directly in our code.

 

My apologies for hijacking the thread. I'll stop now.

Share this post


Link to post
57 minutes ago, JonRobertson said:

Word of caution, TLightweightMREW is not merely a lightweight equivalent of TMultiReadExclusiveWriteSynchronizer as it has different behavior. 

 

Most notably, write lock is not reentrant on TLightweightMREW and acquiring write lock from the same thread twice will cause deadlocks on Windows platform and will raise exception on other platforms.

 

When it comes to TMultiReadExclusiveWriteSynchronizer it is implemented as MREW only on Windows and on other platforms it is exclusive lock.

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×