Recent

Author Topic: Howto send Record to thread via Pointer?  (Read 1718 times)

jan741

  • New Member
  • *
  • Posts: 15
Howto send Record to thread via Pointer?
« on: December 07, 2022, 02:22:51 pm »
I need send a record(containing numeral and string) to multiple threads. How to?

Tried different things, this doesn't work:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : myrec;
  54. begin
  55.  
  56.   Writeln('thread ', longint(p^.i), 'started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',p^.i,' finished with',p^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66. end;
  67.  
  68. procedure TForm1.Button1Click(Sender: TObject);
  69. begin
  70.  
  71. end;
  72.  
  73. procedure TForm1.Button2Click(Sender: TObject);
  74. var
  75.   r : myrec;
  76.   x : longint;
  77. begin
  78.    finished:=0;
  79.    r.s := ' test ';
  80.    for x :=1 to threadcount do
  81.      r.i := x;
  82.      BeginThread(@f,@r);
  83.    while finished<threadcount do ;
  84.    Writeln(finished);
  85. end;
  86.  
  87.  
  88. end.

Zvoni

  • Hero Member
  • *****
  • Posts: 2300
Re: Howto send Record to thread via Pointer?
« Reply #1 on: December 07, 2022, 02:51:08 pm »
errr.... is there a Begin/End missing in the For-loop?
The For-Loop as it stands only executes line 81 for 100 times
BeginThread is only executed once (with r.i=100) because it's outside the For-Loop
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #2 on: December 07, 2022, 03:05:57 pm »
Yes thanks.
That was also a problem. I'm more used to {} or inclinations(python)  8-)

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #3 on: December 07, 2022, 03:16:07 pm »
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : ^myrec;
  54. begin
  55.   thisrec := Pmyrec(p);
  56.   Writeln('thread ', longint(thisrec^.i), ' started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66.   writeln('end of function');
  67. end;
  68.  
  69. procedure TForm1.Button1Click(Sender: TObject);
  70. begin
  71.  
  72. end;
  73.  
  74. procedure TForm1.Button2Click(Sender: TObject);
  75. var
  76.   r : myrec;
  77.   x : longint;
  78. begin
  79.    finished:=0;
  80.    r.s := ' test ';
  81.    for x :=1 to threadcount do
  82.      begin
  83.        r.i := x;
  84.        BeginThread(@f,@r);
  85.        writeln('ready for nex t');
  86.      end;
  87.    while finished<threadcount do ;
  88.    Writeln('goopend ' + IntToStr(finished));
  89. end;
  90. end.

mika

  • Full Member
  • ***
  • Posts: 102
Re: Howto send Record to thread via Pointer?
« Reply #4 on: December 07, 2022, 04:44:32 pm »
Code: Pascal  [Select][+][-]
  1. program Unit1;
  2. {$apptype console}
  3. {$mode objfpc}{$H+}
  4.  
  5.  
  6. uses
  7. {$ifdef UNIX}  cthreads, {$endif}
  8.    Classes, SysUtils;
  9.  
  10. const
  11.       threadcount = 100;
  12.       stringlen = 10000;
  13.  
  14. type
  15.   myrec = record
  16.     i : longint;
  17.     s : string;
  18.   end;
  19.  
  20.   Pmyrec = ^myrec;
  21.  
  22. var
  23.   finished : longint;
  24.  
  25. threadvar
  26.          thri : ptrint;
  27.  
  28.  
  29. function f(p : Pointer) : ptrint;
  30.  
  31. var
  32.   s : ansistring;
  33.   thisrec : ^myrec;
  34. begin
  35.   thisrec := Pmyrec(p);
  36.   Writeln('thread ', longint(thisrec^.i), ' started');
  37.   thri:=0;
  38.   while (thri<stringlen) do
  39.     begin
  40.     s:=s+'1';
  41.     inc(thri);
  42.     end;
  43.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  44.   InterLockedIncrement(finished);
  45.   f:=0;
  46.   writeln('end of function');
  47. end;
  48.  
  49. procedure button;
  50. var
  51.   r : array [1..threadcount] of myrec;
  52.   x : longint;
  53. begin
  54.    finished:=0;
  55.  
  56.    for x :=1 to threadcount do
  57.      begin
  58.        r[x].s := ' test ';
  59.        r[x].i := x;
  60.        BeginThread(@f,@r[x]);
  61.        writeln('ready for nex t');
  62.      end;
  63.    while finished<threadcount do ;
  64.    Writeln('goopend ' + IntToStr(finished));
  65. end;
  66.  
  67. begin
  68.      button;
  69. end.
In way you are using record in thread, you have to have an original record in main thread. I made array of record and passed new unique record to new thread.
« Last Edit: December 07, 2022, 04:48:49 pm by mika »

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Howto send Record to thread via Pointer?
« Reply #5 on: December 08, 2022, 08:34:39 am »
I need send a record(containing numeral and string) to multiple threads. How to?
Since you are using forms, the simplest way is to use QueueAsyncCall:
https://forum.lazarus.freepascal.org/index.php/topic,61391.msg461751.html#msg461751
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

MarkMLl

  • Hero Member
  • *****
  • Posts: 6647
Re: Howto send Record to thread via Pointer?
« Reply #6 on: December 08, 2022, 09:52:41 am »
In any event, you need to interlock the data transfer so that the sender knows when the data has been accepted, and enforce some form of critical section mechanism on any part of that which can be accessed by more than one thread.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 2300
Re: Howto send Record to thread via Pointer?
« Reply #7 on: December 08, 2022, 09:58:04 am »
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : ^myrec;
  54. begin
  55.   thisrec := Pmyrec(p);
  56.   Writeln('thread ', longint(thisrec^.i), ' started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66.   writeln('end of function');
  67. end;
  68.  
  69. procedure TForm1.Button1Click(Sender: TObject);
  70. begin
  71.  
  72. end;
  73.  
  74. procedure TForm1.Button2Click(Sender: TObject);
  75. var
  76.   r : myrec;
  77.   x : longint;
  78. begin
  79.    finished:=0;
  80.    r.s := ' test ';
  81.    for x :=1 to threadcount do
  82.      begin
  83.        r.i := x;
  84.        BeginThread(@f,@r);
  85.        writeln('ready for nex t');
  86.      end;
  87.    while finished<threadcount do ;
  88.    Writeln('goopend ' + IntToStr(finished));
  89. end;
  90. end.
Adding to avra and Mark, but concentrating on your initial question:
Line 53 should not be a Pointer-Type, but the Record-Type directly! Only then you would get a local copy
And Line 55 would then read
 thisrec := Pmyrec(p)^;
and later inside the f-function you wouldn't need to dereference
« Last Edit: December 08, 2022, 09:59:37 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #8 on: December 08, 2022, 05:32:26 pm »
I tested it, but doesn't work. Reason; you don't have control when the local assignment happens of every threadversion of thisrec. So, what you got as output are no aligned numbers less than 100 but mostly you get 100. The main-thread loop keeps running before most treads even start initializing their version of thisrec. When they start in most cases r.i = 100
My solution was working with the "new" keyword and making copy's on the heap, passing the pointer to -and disposing them in- each tread, a similar solution of mika instead he works with a stack-array, what creates less overhead.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6647
Re: Howto send Record to thread via Pointer?
« Reply #9 on: December 08, 2022, 07:18:13 pm »
My solution was working with the "new" keyword and making copy's on the heap, passing the pointer to -and disposing them in- each tread, a similar solution of mika instead he works with a stack-array, what creates less overhead.

What makes you confident that the memory manager is thread-safe in the way you're using it?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

alpine

  • Hero Member
  • *****
  • Posts: 1032
Re: Howto send Record to thread via Pointer?
« Reply #10 on: December 09, 2022, 12:11:16 am »
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Consider the following:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. const
  11.   threadcount = 100;
  12.   stringlen = 10000;
  13.  
  14. type
  15.  
  16.   { TForm1 }
  17.  
  18.   TForm1 = class(TForm)
  19.     Button1: TButton;
  20.     Button2: TButton;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.   private
  24.  
  25.   public
  26.  
  27.   end;
  28.  
  29.   myrec = record
  30.     i : longint;
  31.     s : string;
  32.   end;
  33.  
  34.   Pmyrec = ^myrec;
  35.  
  36. var
  37.   Form1: TForm1;
  38.   finished : longint;
  39.  
  40. threadvar
  41.   thri : ptrint;
  42.  
  43. implementation
  44.  
  45. {$R *.lfm}
  46.  
  47. uses
  48.   Windows; // for AllocConsole
  49.  
  50. type
  51.  
  52.   { TMyThread }
  53.  
  54.   TMyThread = class(TThread)
  55.   private
  56.     fr: myrec; // Private copy of myrec
  57.   public
  58.     constructor Create(var r: myrec);
  59.     procedure Execute; override;
  60.   end;
  61.  
  62. { TForm1 }
  63.  
  64. function f(p : Pointer) : ptrint;
  65. var
  66.   s : ansistring;
  67.   thisrec : ^myrec;
  68. begin
  69.   thisrec := Pmyrec(p);
  70.   Writeln('thread ', longint(thisrec^.i), ' started');
  71.   thri:=0;
  72.   while (thri<stringlen) do
  73.     begin
  74.     s:=s+'1';
  75.     inc(thri);
  76.     end;
  77.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  78.   InterLockedIncrement(finished);
  79.   f:=0;
  80.   writeln('end of function');
  81. end;
  82.  
  83. { TMyThread }
  84.  
  85. constructor TMyThread.Create(var r: myrec);
  86. begin
  87.   fr := r; // Preserve into the private copy
  88.   FreeOnTerminate := True; // Free when terminated
  89.   inherited Create(False); // Create and run
  90. end;
  91.  
  92. procedure TMyThread.Execute;
  93. begin
  94.   f(@fr);  // Call f() with a pointer to the private copy
  95. end;
  96.  
  97. procedure TForm1.Button1Click(Sender: TObject);
  98. var
  99.   r : myrec;
  100.   x : longint;
  101. begin
  102.    finished:=0;
  103.    r.s := ' test ';
  104.    for x :=1 to threadcount do
  105.      begin
  106.        r.i := x;
  107.        BeginThread(@f,@r);
  108.        writeln('ready for nex t');
  109.      end;
  110.    while finished<threadcount do ;
  111.    Writeln('goopend ' + IntToStr(finished));
  112. end;
  113.  
  114. procedure TForm1.Button2Click(Sender: TObject);
  115. var
  116.   r : myrec;
  117.   x : longint;
  118. begin
  119.    finished:=0;
  120.    r.s := ' test ';
  121.    for x :=1 to threadcount do
  122.      begin
  123.        r.i := x;
  124.        TMyThread.Create(r);
  125.        writeln('ready for nex t');
  126.      end;
  127.    while finished<threadcount do ;
  128.    Writeln('goopend ' + IntToStr(finished));
  129.  
  130. end;
  131.  
  132. // Following lines are to make WriteLn work on Windows GUI App
  133. initialization
  134.   // Allocate console in Windows
  135.   AllocConsole;      // in Windows unit
  136.   IsConsole:=True;   // in System unit
  137.   SysInitStdIO;      // in System unit
  138. end.
  139.  

Here TMyThread constructor is used to make a private copy of the myrec before actually starting the thread (line 89). Then Execute calls your f() function with a pointer to the private copy (line 94). TMyThread instances were automatically freed because of line 88.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

alpine

  • Hero Member
  • *****
  • Posts: 1032
Re: Howto send Record to thread via Pointer?
« Reply #11 on: December 09, 2022, 12:16:29 pm »
@jan741
The post is in the "Beginners" section, so I'm assuming you're coming with a different background (Python was mentioned). As multi-threading is known to be a bit of a tricky issue, if you have any questions about FPC MT support (including TThread), ask them and there are enough supportive members to answer you.

I suspect there are some misunderstandings about FPC memory/variable allocation, too.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

MarkMLl

  • Hero Member
  • *****
  • Posts: 6647
Re: Howto send Record to thread via Pointer?
« Reply #12 on: December 09, 2022, 12:25:40 pm »
As multi-threading is known to be a bit of a tricky issue, if you have any questions about FPC MT support (including TThread), ask them and there are enough supportive members to answer you.

I suspect there are some misunderstandings about FPC memory/variable allocation, too.

Yes, agreed. I remember though discussing threaded memory allocation on the mailing list with Jonas, and from memory the gist was that the RTL tries to simulate per-thread heaps... but that there were potential problems if trying to do too much clever cross-thread stuff.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #13 on: December 09, 2022, 05:15:03 pm »
Ok thanks

jan741

  • New Member
  • *
  • Posts: 15
Re: Howto send Record to thread via Pointer?
« Reply #14 on: December 10, 2022, 11:27:44 pm »
Initialization of thread fails. See code for more details. Program ends in endless loop(assembly code not concerning of code bellow, but compiler made stuff)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 50;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.     procedure deproc(input_: PtrInt);
  27.   public
  28.     { public declarations }
  29.   end;
  30.  
  31.   myrec = record
  32.     i : longint;
  33.     s : string;
  34.   end;
  35.  
  36.  TThread1 = class(TThread)
  37.  public
  38.    procedure Execute; override;        
  39.    constructor create(inp:myrec);    
  40.  private
  41.    inv:myrec;
  42.  end;
  43.  
  44.  Pmyrec = ^myrec;
  45.  ps = ^ansistring;
  46. var
  47.   Form1: TForm1;
  48.   Thread1:TThread1;
  49.   finished : longint;
  50.   y : longint;
  51.   nr : integer;
  52. threadvar
  53.   thri : ptrint;
  54.  
  55. implementation
  56. {$R *.lfm}
  57.  
  58.  
  59. function f(p : Pointer) : ptrint;
  60.  
  61. var
  62.   s : ansistring;
  63.   pta:ps;
  64.   thisrec : myrec;
  65. begin
  66.   thisrec := Pmyrec(p)^;
  67.   Writeln('thread ', thisrec.i, ' started');
  68.   thri:=0;
  69.   new(pta);
  70.   pta^ :='zodiac';
  71.   //sleep(100);
  72.   while (thri<stringlen) do
  73.     begin
  74.  
  75.     inc(thri);
  76.     //if (thisrec.i = 1) then Application.QueueAsyncCall(@Form1.deproc, PtrInt(pta));
  77.  
  78.     end;
  79.   Writeln('thread ',thisrec.i,' finished with',thisrec.s);
  80.   InterLockedIncrement(finished);
  81.   f:=0;
  82.  
  83. end;
  84.  
  85. { TForm1 }
  86. procedure TForm1.Button1Click(Sender: TObject);
  87. begin
  88.   y:= 1;
  89. end;
  90.  
  91. procedure TForm1.Button2Click(Sender: TObject);
  92. var
  93.   r: myrec;
  94.   pr: ^myrec;
  95.   x : longint;
  96. begin
  97.    finished:=0;
  98.    r.s := ' test ';
  99.    for x :=1 to 2 do
  100.      begin
  101.        r.i:=x;
  102.        //BeginThread(@f,@r[x]);
  103.        Thread1.create(r);                    // <<< after loop, program hungs
  104.      end;
  105.    while finished<threadcount do ;
  106. end;
  107.  
  108. procedure TForm1.deproc(input_:PtrInt);
  109. var
  110.   s: ansistring;
  111.  
  112. begin
  113.    s := ps(input_)^;
  114.    write(s);
  115.    dispose(ps(input_));
  116. end;
  117.  
  118. { Thread1 }
  119.  
  120. constructor TThread1.create(inp:myrec);     // <<----fails: in assembly, code between begin and end is never executed(when opening assembler-window and step into.
  121. begin
  122.    inv := inp;
  123.    writeln('init tread', inv.i);
  124.    inherited Create(False);  
  125.    writeln('ready for next');
  126. end;
  127.  
  128. procedure TThread1.Execute;
  129. begin
  130.    f(@inv);
  131. end;
  132.  
  133. end.

 

TinyPortal © 2005-2018