Recent

Author Topic: Thread safe classes  (Read 14309 times)

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Thread safe classes
« on: September 19, 2018, 10:46:26 am »
Dear all,

I am working on a multithreaded application.
We had some issues since we used TStringList in our threads and then we got that TStringList is not thread safe.
My questions are:
1. is TStringList not thread safe because of the use of randomized quicksort? (we had issues with TStringList only when we used sorted TStringList)
2. since TFPObjectList does not use randomized quicksort is it thread safe?

And finally, there is a list of thread safe classes and/or functions and procedures?

Andrea

sash

  • Sr. Member
  • ****
  • Posts: 366
Re: Thread safe classes
« Reply #1 on: September 19, 2018, 12:46:16 pm »
Hello,

First I don't know exactly about TStringList and TFPObjectList, but isn't it easy to make ANY code thread-safe by simple use of try-finally-CriticalSection or whatever guard mechanism that suits your application logic better?

IMHO there's no point to make base classes (unless they specifically designed for multi-threading) thread-safe, because it will add performance penalty in cases where you don't need multi-threading.
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #2 on: September 19, 2018, 02:19:30 pm »
I already use CriticalSection when I know that I need it but what I would like to know is when I need to use it and when it is not needed.

Now that I know that Random and TStringList are not thread safe I can avoid using it, using a thread safe version or use CriticalSection.

A list classes or a list of funciotn/procedure thread-safe or not could be useful.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Thread safe classes
« Reply #3 on: September 19, 2018, 02:55:50 pm »
Specialize a type, not a var.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Thread safe classes
« Reply #4 on: September 20, 2018, 04:30:35 pm »
Neither TStringList nor TFPObjectList are thread safe. The problem is not the sorting, but if you have multiple Add()-calls (or Delete() or anything else that modifies the list) happening at roughly the same time from different threads the internal list could become corrupted. Such calls and also get-calls would need to be protected with critical sections.

If you don't need the full capabilities of TStringList (LoadFromFile(), Sort(), etc.) you can use the generic TThreadList<> from Generics.Collections (part of 3.1.1 and newer or downloadable as an addon for 3.0.4).

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #5 on: September 20, 2018, 05:26:43 pm »
In order to clarify my issue.
I do not share an instance of TStringList among threads, but my threads use their own instances of TStringList.
Should I avoid to do this? Should I remove all my TStringList instances from all my threads or use criticalsecction?
I am asking since I noticed different results only if I use Random or Sort, otherwise my application seems to works fine.

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Thread safe classes
« Reply #6 on: September 20, 2018, 07:11:10 pm »
I am not really an expert, but ...

If each single thread uses its own TStringList then you shouldn't have any problem unless various threads may access class-invariants (class constants & fields, etc. i.e. parts of the class shared among all instances) at the same time; these you must protect.

However, if various threads must have access to a single TStringList (or almost any other object, for that matter) then it's best to protect the accesses with critical sections (or other synchronization measure).

If Random and Sort are giving you problems, I would check very carefully both your code and those methods' implementation.

My 2 cents.  :)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

HeavyUser

  • Sr. Member
  • ****
  • Posts: 397
Re: Thread safe classes
« Reply #7 on: September 20, 2018, 08:38:23 pm »
In order to clarify my issue.
I do not share an instance of TStringList among threads, but my threads use their own instances of TStringList.
That's not possible. if you do not share the string list between threads then you do not have multithreading problems.
How do you add items to that list? How do you read?

Should I avoid to do this? Should I remove all my TStringList instances from all my threads or use criticalsecction?
I am asking since I noticed different results only if I use Random or Sort, otherwise my application seems to works fine.
Its probably the sort that made the problem visible you still have the problem with out it, the timing is to tight to surface it self with out the sort.

Please provide a small sample with a thread that uses a non shared string list and that how that string list is updated.



PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Thread safe classes
« Reply #8 on: September 21, 2018, 03:27:57 pm »
In order to clarify my issue.
I do not share an instance of TStringList among threads, but my threads use their own instances of TStringList.
Should I avoid to do this? Should I remove all my TStringList instances from all my threads or use criticalsecction?
I am asking since I noticed different results only if I use Random or Sort, otherwise my application seems to works fine.
If the TStringList instances are not shared between the threads then you don't have a problem. If however it might happen that one thread calls a method of another thread which in turn modifies the TStringList instance of that thread then you need to add protection inside that method.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #9 on: September 22, 2018, 11:16:27 am »
My issue seems to be related only to the Random function included in the TStringList sort (quicksort, random is used to define the pivot).

My threads uses random function to hash strings, all the random stuff are included in CriticalSection so I supposed to be safe, but if I use the sort method of a not shared TStringList my thread fails.

suppose that my thread is doing something like:

Code: Pascal  [Select][+][-]
  1.   EnterCriticalSection(CriticalSection);
  2.   RandSeed:= Hash(mystring);
  3.   try
  4.         // do some stuff including random
  5.   finally
  6.     LeaveCriticalSection(CriticalSection);
  7.   end;

It should be safe, but if I use TStringList.Sort somewhere else (not shared TStringList) my random sequence will be broken.
It does not happen if I use TFPObjectList.Sort since TFPObjectList quicksort does not use Random to define the pivot.

So basically TStringList sorting is not affected if I use Random somewhere else but my thread using random function, even if in critical section, is affected by TStringList.Sort.


PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Thread safe classes
« Reply #10 on: September 22, 2018, 08:15:44 pm »
FPC's random number generator is not threadsafe, so that means that you need to protect all calls to it. Using transivity that also means that you need to protect all calls to TStringList.Sort.
I'll check back with the other devs whether we want to do something about that, but for now (and at least with the 3.0.x series) you need to protect that yourself.
« Last Edit: September 22, 2018, 08:48:18 pm by PascalDragon »

SymbolicFrank

  • Hero Member
  • *****
  • Posts: 1313
Re: Thread safe classes
« Reply #11 on: September 23, 2018, 04:40:23 am »
Q: How do I share a random variable/container between multiple threads?
A: Don't do that. It's problematic.

Q: But, I lock and unlock (or sync) every access to that var / list, why is it still problematic?
A: Lock and unlock (or critical section, mutexes, etc) are the most invasive and expensive functions you can execute. And they're mutually exclusive: the second lock on the same object will deadlock your app. Try not to use them.

Q: But, I have to tell the main app from multiple threads what to display, and when they're finished!
A: Yes, that's hard. Think really hard about that.

Q: But, I HAVE to communicate!
A: Have the main app create a thread-safe queue, pass the address to the threads and use that. It's your only safe bet.

Q: I really need multi-directional communications...
A: Use sockets. Quite complex if you need a manager (and you probably do), but it's the only safe way. And hey, it works on every platform!


Edit: I should make a threading class and document. And probably some other ones as well.
« Last Edit: September 23, 2018, 05:04:05 am by SymbolicFrank »

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Thread safe classes
« Reply #12 on: September 23, 2018, 07:46:24 am »
@apeoperaio,

Random is not threadsafe. Moreover, Critical sections will *not* provide the same sequence for each thread. Your application obviously depends on that sequence. The best solution was provided to you by Thaddy (nice code, Thaddy. Thanks for sharing.). Check his answer.

You'll simply need to add his unit tsrandom to your uses section, and prepend every call to Random with TsRand.Random. The same for Randseed, replace it with TsRand.Randseed. This way, TStringList.Sort will not affect the sequence, and you will not need to use critical sections. Your code will become:
Code: Pascal  [Select][+][-]
  1.   TsRand.RandSeed:= Hash(mystring);
  2.         // do some stuff including random

As simple as that.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Thread safe classes
« Reply #13 on: September 23, 2018, 02:37:46 pm »
If you communicate simple values have a look at the interlocked* family of functions. These are lightweight.
As are events and semaphore's (slightly less).

But for the random: yes, you can use it like engkin wrote. You can ignore the comments I wrote about randomize: it is safe. And in your case you do not need it: just seed per thread with the same value. tsrandom generates the exact same random values as the default fpc random.

Have a look at:
https://www.freepascal.org/~michael/articles/lazthread/lazthread.pdf written by Michael van Canneyt.
Sources: https://www.freepascal.org/~michael/articles/lazthread/lazthread.zip

and:
http://nickhodges.com/MultiThreadingInDelphi/ToC.html written by Martin Harvey.
Which is still absolutely the best tutorial about multithreading and also compatible with FreePascal.
« Last Edit: September 23, 2018, 02:47:59 pm by Thaddy »
Specialize a type, not a var.

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Thread safe classes
« Reply #14 on: September 23, 2018, 10:41:00 pm »
Q: But, I have to tell the main app from multiple threads what to display, and when they're finished!
A: Yes, that's hard. Think really hard about that.
It doesn't have to be. When we want for example to update GUI elements from one of the threads, QueueAsyncCall is very nice. Here is a nice example to transfer a whole record to the main thread: https://forum.lazarus.freepascal.org/index.php/topic,38851.msg265181.html#msg265181

Good thing is that QueueAsyncCall is non-blocking and it does very usefull serialization so there is no need for syncing at all - and that feature was heavily used in my update to MultiLog where many threads update the same memo on a form and also write to the same log file without any problem.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

 

TinyPortal © 2005-2018