Recent

Author Topic: Thread safe classes  (Read 14298 times)

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #15 on: September 24, 2018, 10:36:15 am »
Thanks to all! All the shared information is very useful.
So, it's not a matter of tstringlist but of Random. If I use random in a thread (even in critical section) it must be thread safe, since I cannot have the total control of other calls to random in all the libraries.

The thread-safe random implementation is very useful.

http://forum.lazarus.freepascal.org/index.php/topic,35050.msg242571.html#msg242571 forum threadsafe random
.

Thanks a lot Thaddy!
What about the license of your unit? Could I use your unit in my projects?

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #16 on: September 24, 2018, 11:22:21 am »
I am trying Thaddy implementation but when I run a project including tsrandom using DebugMode I get:
Run Error (201)
during  TThreadSafeRandom.create;

Code: Pascal  [Select][+][-]
  1. class constructor TThreadSafeRandom.create;
  2. begin
  3.   initialize(0);  // <-- the error is raised here
  4.   mtOldRandSeed := 0;
  5. end;


Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Thread safe classes
« Reply #17 on: September 24, 2018, 01:02:15 pm »
That must be something else: how can 0 give a rangecheck error under normal circumstances?
Is the hash value within a 32 bit range? Because FPC's random and subsequently mine demands a 32 bit value to seed.
Can you give me some reproducible small example?
Did you modify my code? (I wouldn't do that).

Btw: if that's the case I can not change it, because FPC random compatibility and standards compliance to MT19937 would be gone.
You need to hard cast such a hash to cardinal (which will chop off lsb's)
And to be sure: the seed is a cardinal which is always a positive value. That's how a mersenne twister is supposed to work according to its standard MT19937 or MT19937-2 specification.
« Last Edit: September 24, 2018, 01:16:10 pm by Thaddy »
Specialize a type, not a var.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #18 on: September 24, 2018, 01:19:00 pm »
Attached a very simple example. A new project using tsrandom, I just added the unit to the lpr.
I didn't modify your unit (just replaced inteface with interface).

In order to raise the error check the Range (-Cr) option.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Thread safe classes
« Reply #19 on: September 25, 2018, 06:18:25 am »
I can not reproduce this with your example.
Tested Laz1.8.4+FPC 3.0.4 and Laz 2.1.0 rev 50153+FPC 3.3.1-rev 39797 under armhf-linux and windows64.

Apart from the copy/paste error this would have shown up, I know quite a few people use it.
What is your configuration?
« Last Edit: September 25, 2018, 06:22:04 am by Thaddy »
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Thread safe classes
« Reply #20 on: September 25, 2018, 07:35:35 am »
I can not reproduce this with your example.
Reproduced Win64.
Add typecast to DWORD in TThreadSafeRandom.Initialize:
Code: Pascal  [Select][+][-]
  1. for i := 1 to Pred(N) do
  2.   mt[i] := DWORD(F * mt[i - 1] xor (mt[i - 1] shr 30)) + i;
  3.  
and the error disappears.

By the way, remove the inline directives and the code will be better.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #21 on: September 25, 2018, 09:03:20 am »
Add typecast to DWORD in TThreadSafeRandom.Initialize:
Code: Pascal  [Select][+][-]
  1. for i := 1 to Pred(N) do
  2.   mt[i] := DWORD(F * mt[i - 1] xor (mt[i - 1] shr 30)) + i;
  3.  
and the error disappears.

It works now! Before I tried on Win64, Linux64 and OSX64 and I always get Range Check error.

Could you explain:
By the way, remove the inline directives and the code will be better.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #22 on: September 25, 2018, 09:37:25 am »
Sorry for my enthusiasm but it does not work. It does not raise the range check error but the random sequence is corrupted.

@Thaddy I get range check error on Win64, Linux64 and OSX64. How can I help to reproduce it?

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Thread safe classes
« Reply #23 on: September 25, 2018, 06:01:56 pm »
Sorry for my enthusiasm but it does not work. It does not raise the range check error but the random sequence is corrupted.
How did you determine the random sequence is corrupted?
I generated the first 20 numbers with DWORD cast and without DWORD cast - they are the same.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #24 on: September 25, 2018, 06:24:02 pm »
I get different results when using tsrandom in multiple threads.
I will try to prepare a sample project.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Thread safe classes
« Reply #25 on: September 25, 2018, 06:26:52 pm »
You need to have the same seed for every thread. The results are the same. I have an example. post it later. This is well documented in the source.
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Thread safe classes
« Reply #26 on: September 25, 2018, 06:41:03 pm »
Looked closely code and found shortcomings:
1. The class constructor initializes once during the lifetime of the application. Thread variables need to be initialized on each thread. That is, the class constructor is not suitable for them.
2. From FPC documentation "Threadvars should be used sparingly: There is an overhead for retrieving or setting the variable’s value". It's true. Every access to thread variables is very expensive. It is better to declare one complex variable and get its address for manipulation.
3. Sealed class with class static methods - it's only c++ workaround for FPC units.

I propose a correction in which:
1. Removed class wrapper and changed everything to normal unit procedures.
2. Made optimize access to thread variables through a pointer and the overall structure.
3. Did the test initialization.
4. Magic constants with insignificant names, which occur only once, are expanded into the code (It's still not clear what it is).
The code logic has not been changed except for the standard formatting.

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #27 on: September 25, 2018, 08:30:29 pm »
You need to have the same seed for every thread. The results are the same. I have an example. post it later. This is well documented in the source.

Could you post your example?

apeoperaio

  • Sr. Member
  • ****
  • Posts: 272
Re: Thread safe classes
« Reply #28 on: September 26, 2018, 09:07:14 am »
Finally I got my threads working!
I modified the code posted by @ASerge adding a missing parenthesis:

Code: Pascal  [Select][+][-]
  1.  
  2. for i := 1 to Pred(N) do
  3.     mt[i] := DWORD(F * (mt[i - 1] xor (mt[i - 1] shr 30))) + i;
  4.  

Now I get no range check errors and my threads give the same result.
Anyway I was not able to have the same results using the second version posted by @ASerge.

Zoran

  • Hero Member
  • *****
  • Posts: 1829
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Thread safe classes
« Reply #29 on: September 26, 2018, 02:03:52 pm »
3. Sealed class with class static methods - it's only c++ workaround for FPC units.

What? Static methods do not exist in C++. You saw these in Java.
A C++ class can only have instance methods, no static methods. There are global functions only, but C++ gives you the feature of packing the global functions and variables in "namespaces".
As FPC lacks the beautiful C++ feature of namespaces, "static" classes come as a solution for this ("static class" is java terminology for a class with class variables and methods only).

And no, Pascal units are no substitution for namespaces. Unfortunately, units do not force good practice of qualifying identifiers when used from other units.
Wrapping routines (and variables too) in "static" classes, is a very good practice. It is a way to force qualifying function calls (and variables).

 

TinyPortal © 2005-2018