Recent

Author Topic: [SOLVED] Passing a variable exclusive to a thread  (Read 1508 times)

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
[SOLVED] Passing a variable exclusive to a thread
« on: March 31, 2021, 01:40:37 am »
Using lazarus 2.0.10 on win 8.1

This is pseudo code:

Code: Pascal  [Select][+][-]
  1. TThreadOne = class(TThread)
  2.     procedure Execute; override;
  3.   end;
  4.  
  5. MySpecialVariable : int64;
  6.  
  7. MyThreads: array [1..10] of TThreadOne;
  8.  
  9. For counter 1 to 10 do
  10.    begin
  11.    If Assigned(MyThreads[counter]) then MyThreads[counter].terminate;
  12.    delay(10);
  13.    MyThreads[counter] := TThreadOne.Create(true);
  14.    MyThreads[counter].FreeOnTerminate:=true;
  15.    MyThreads[counter].Start;
  16.    end
  17.  
  18. procedure TThreadOne.Execute;    
  19. Begin
  20. MySpecialvariable {do many things with this variable}
  21. End

My question: How i could pass a different MySpecialvariable value to each thread?
Thanks.
« Last Edit: April 01, 2021, 10:25:29 pm by torbente »
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Passing a variable exclusive to a thread
« Reply #1 on: March 31, 2021, 01:52:38 am »
Depends on what you are tring to do, use Threadvar, or have similar array for that variable, one for each thread and give the thread its index in that array.

As a side note, this line is not safe if you have your threads started with FreeOnTerminate is True:
Code: Pascal  [Select][+][-]
  1.    If Assigned(MyThreads[counter]) then MyThreads[counter].terminate;

and the thead will not end instantly after this call.
« Last Edit: March 31, 2021, 01:57:43 am by engkin »

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
Re: Passing a variable exclusive to a thread
« Reply #2 on: March 31, 2021, 03:08:43 am »
Depends on what you are tring to do, use Threadvar, or have similar array for that variable, one for each thread and give the thread its index in that array.

As a side note, this line is not safe if you have your threads started with FreeOnTerminate is True:
Code: Pascal  [Select][+][-]
  1.    If Assigned(MyThreads[counter]) then MyThreads[counter].terminate;

and the thead will not end instantly after this call.

AFAIK, threadvar always assign the same value to all threads and then it becomes a thread-local var. I want that each thread have a different value from its beggining. Or how i could detect, inside the thread, what index of the thread array it is?

Code: Pascal  [Select][+][-]
  1. If Assigned(MyThreads[counter]) then
  2.    try
  3.    MyThreads[counter].terminate;
  4.    finally
  5.    end;

It is safe now?

« Last Edit: March 31, 2021, 03:11:45 am by torbente »
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Passing a variable exclusive to a thread
« Reply #3 on: March 31, 2021, 04:58:14 am »
No, not safe.

As for the variable, add it (the index)  to the thread class TThreadOne
« Last Edit: March 31, 2021, 05:31:42 am by engkin »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5481
  • Compiler Developer
Re: Passing a variable exclusive to a thread
« Reply #4 on: March 31, 2021, 08:53:07 am »
It is safe now?

No. General rule is: if FreeOnTerminate is set to True do not access the thread instance from outside anymore. The only exception is if you for sure know that the thread is running a loop and it will only terminate if you trigger some action (e.g. calling TThread.Terminate).

AFAIK, threadvar always assign the same value to all threads and then it becomes a thread-local var. I want that each thread have a different value from its beggining. Or how i could detect, inside the thread, what index of the thread array it is?

If you want each thread to have a different value, why don't you simply pass it in as part of the constructor?

Code: Pascal  [Select][+][-]
  1. type
  2.   TThreadOne = class(TThread)
  3.   private
  4.     fMySpecialVariable: Int64;
  5.   protected
  6.     procedure Execute; override;
  7.   public
  8.     constructor Create(aMySpecialVariable: Int64);
  9.   end;
  10.  
  11. constructor TThreadOne.Create(aMySpecialVariable: Int64);
  12. begin
  13.   inherited Create(True);
  14.   fMySpecialVariable := aMySpecialVariable;
  15. end;

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
Re: Passing a variable exclusive to a thread
« Reply #5 on: March 31, 2021, 02:26:52 pm »
Quote
Quote from: torbente on Today at 03:08:43 am
It is safe now?

No. General rule is: if FreeOnTerminate is set to True do not access the thread instance from outside anymore. The only exception is if you for sure know that the thread is running a loop and it will only terminate if you trigger some action (e.g. calling TThread.Terminate).

Ok i removed it. Since threads are a loop (repeat... until finishthread= true) i just added this to verify that all threads are terminated before creating it again:

Code: Pascal  [Select][+][-]
  1. finishthread := true;
  2. Delay(100);
  3. finishthread := false;

For the variable, i will try using counter as a global variable so each thread can get its value.
Something like this:

Code: Pascal  [Select][+][-]
  1. TThreadOne = class(TThread)
  2.     procedure Execute; override;
  3.   end;
  4.  
  5. {Global variables}
  6.  
  7. MySpecialVariable : int64;
  8. TCounter : integer;
  9. StropThread : boolean;
  10.  
  11. MyThreads: array [1..10] of TThreadOne;
  12.  
  13. Procedure LaunchThreads();
  14. begin
  15. StropThread := true;
  16. delay(100)
  17. StropThread := false;
  18. For TCounter := 1 to 10 do
  19.    begin
  20.    MyThreads[TCounter] := TThreadOne.Create(true);
  21.    MyThreads[TCounter].FreeOnTerminate:=true;
  22.    MyThreads[TCounter].Start;
  23.    Delay(10);
  24.    end
  25. end;
  26.  
  27. procedure TThreadOne.Execute;  
  28. var
  29.   MySpecialvariable : integer;
  30. Begin
  31. MySpecialvariable := TCounter;
  32. Repeat
  33. {use of MySpecialvariable}
  34. until StropThread;
  35. End
  36.  

This should work, but threads are so complex that i will try it first.

Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Passing a variable exclusive to a thread
« Reply #6 on: March 31, 2021, 04:23:26 pm »
Code: Pascal  [Select][+][-]
  1. //...
  2. Procedure LaunchThreads();
  3. begin
  4. StropThread := true;
  5. delay(100)
  6. StropThread := false;
  7. For TCounter := 1 to 10 do
  8.    begin
  9.    MyThreads[TCounter] := TThreadOne.Create(true);
  10.    MyThreads[TCounter].FreeOnTerminate:=true;
  11.    MyThreads[TCounter].Start;
  12.    Delay(10);
  13.    end
  14. end;
  15.  
  16. procedure TThreadOne.Execute;  
  17. var
  18.   MySpecialvariable : integer;
  19. Begin
  20. MySpecialvariable := TCounter;
  21. Repeat
  22. {use of MySpecialvariable}
  23. until StropThread;
  24. End
  25.  

This should work, but threads are so complex that i will try it first.
In this case, all threads can get the same value of MySpecialVariable.
In addition, since the threads itself are released, why do you need an array of references to it's?
Example:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE OBJFPC}
  3.  
  4. uses Classes;
  5.  
  6. type
  7.   TThreadOne = class(TThread)
  8.   protected
  9.     procedure Execute; override;
  10.   public
  11.     constructor Create;
  12.   end;
  13.  
  14. var
  15.   GActiveThreadsCount: Integer = 0;
  16.   GStopWhenNotZero: Integer = 0;
  17.  
  18. constructor TThreadOne.Create;
  19. begin
  20.   inherited Create(False);
  21.   FreeOnTerminate := True;
  22. end;
  23.  
  24. procedure TThreadOne.Execute;
  25. var
  26.   MySpecialVariable: Integer;
  27. begin
  28.   MySpecialVariable := InterlockedIncrement(GActiveThreadsCount);
  29.   try
  30.     while GStopWhenNotZero = 0 do
  31.     begin
  32.       {use of MySpecialVariable}
  33.       Writeln('Thread ID=', ThreadID, ', MySpecialvariable=', MySpecialVariable);
  34.       MySpecialVariable := MySpecialVariable * 10;
  35.       Sleep(250);
  36.     end;
  37.   finally
  38.     InterlockedDecrement(GActiveThreadsCount);
  39.   end;
  40. end;
  41.  
  42. procedure TerminateThreads;
  43. begin
  44.   Writeln('TerminateThreads');
  45.   InterlockedIncrement(GStopWhenNotZero);
  46.   while GActiveThreadsCount > 0 do
  47.     TThread.Sleep(100);
  48. end;
  49.  
  50. procedure LaunchThreads;
  51. var
  52.   i: SizeInt;
  53. begin
  54.   TerminateThreads;
  55.   Writeln('LaunchThreads');
  56.   GStopWhenNotZero := 0;
  57.   for i := 1 to 3 do
  58.     TThreadOne.Create;
  59. end;
  60.  
  61. begin
  62.   LaunchThreads;
  63.   TThread.Sleep(1000);
  64.   TerminateThreads;
  65.   Readln;
  66. end.

torbente

  • Sr. Member
  • ****
  • Posts: 325
    • Noso Main Page
Re: Passing a variable exclusive to a thread
« Reply #7 on: April 01, 2021, 10:19:57 pm »
I have to said that it worked perfectly; leaving a small delay makes that each thread gets a different value from TCounter. This, of course, is a workoarund only, but could help others to implement an easy solution for this situations.

I believe that an easy way to do this should be something like..

Code: Pascal  [Select][+][-]
  1. MyThreads[TCounter] := TThreadOne.Create(true);
  2. MyThreads[TCounter].FreeOnTerminate:=true;
  3. MyThreads[TCounter].SetInternalVariable(MySpecialvariable,1); //or something like that
  4. MyThreads[TCounter].Start;
« Last Edit: April 01, 2021, 10:24:19 pm by torbente »
Noso Cryptocurrency Main Developer
https://github.com/DevTeamNoso/NosoWallet

Gustavo 'Gus' Carreno

  • Hero Member
  • *****
  • Posts: 1120
  • Professional amateur ;-P
Re: [SOLVED] Passing a variable exclusive to a thread
« Reply #8 on: April 21, 2021, 10:57:45 pm »
Hey Torbente,

If you define your TThreadOne as so:
Code: Pascal  [Select][+][-]
  1. type
  2.   TThreadOne = class(TThread)
  3.   private
  4.     FMySpecialVariable: Int64;
  5.   protected
  6.     procedure Execute; override;
  7.   public
  8.     property MySpecialVariable: Int64
  9.       read FMySpecialValiable
  10.       write FMySpecialVariable;
  11.   end;

You can then do:
Code: Pascal  [Select][+][-]
  1. For TCounter := 1 to 10 do
  2.    begin
  3.    MyThreads[TCounter] := TThreadOne.Create(true);
  4.    MyThreads[TCounter].FreeOnTerminate:=true;
  5.    MyThreads[TCounter].MySpecialVariable:= { Whatever you wanna put here };
  6.    MyThreads[TCounter].Start;
  7.    Delay(10);
  8.    end
  9. end;

And that's for a Read/Write property.

If you want a Read Only, then:
Code: Pascal  [Select][+][-]
  1. interface
  2. type
  3.   TThreadOne = class(TThread)
  4.   private
  5.     FMySpecialVariable: Int64;
  6.   protected
  7.     procedure Execute; override;
  8.   public
  9.     constructor Create(CreateSuspended: Boolean; MySpecialVariable: Int64);
  10.  
  11.     property MySpecialVariable: Int64 { This can be committed if there is no use for it outside the thread }
  12.       read FMySpecialValiable;
  13.   end;
  14. implementation
  15. constructor TThreadOne.CreateSuspended: Boolean; MySpecialVariable: Int64);
  16. begin
  17.   inherited Create(CreateSuspended);
  18.   FMySpecialVariable:= MySpecialVariable;
  19. end;

And then:
Code: Pascal  [Select][+][-]
  1. For TCounter := 1 to 10 do
  2.    begin
  3.    MyThreads[TCounter] := TThreadOne.Create(true, { The value of MySpecialVariable });
  4.    MyThreads[TCounter].FreeOnTerminate:=true;
  5.    MyThreads[TCounter].Start;
  6.    Delay(10);
  7.    end
  8. end;
Lazarus 3.99(main) FPC 3.3.1(main) Ubuntu 23.10 64b Dark Theme
Lazarus 3.0.0(stable) FPC 3.2.2(stable) Ubuntu 23.10 64b Dark Theme
http://github.com/gcarreno

 

TinyPortal © 2005-2018