Recent

Author Topic: [SOLVED] Thread Safety?  (Read 22333 times)

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Thread Safety?
« Reply #60 on: February 23, 2018, 01:16:41 pm »
I look at my procedure UpdateProgress and I directly access controls on Form1
When you want to update GUI elements from the thread, QueueAsyncCall is very nice. Someone has already mentioned it, but there are not many examples of using it out there. Here is a nice one 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 MultiLog where many threads update 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

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: Thread Safety?
« Reply #61 on: February 23, 2018, 01:22:46 pm »
Use an atomic operation for the assignments and you are safe.. And that is actually quite easy using the InterlockedExchange(Ptr) family.
In this case, this is superfluous. It's enough to write inherited Create (True) in the constructor, and after FThread.OnTerminate: = @ThreadTerminated; add FThread.Start.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, ComCtrls, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.   TTestThread = class(TThread)
  12.   private
  13.     FInfo: string;
  14.     FMax: Integer;
  15.     FProgress: Integer;
  16.     FCriticalSection: TRTLCriticalSection;
  17.   protected
  18.     procedure Execute; override;
  19.   public
  20.     constructor Create(AMax: Integer; ACriticalSection: TRTLCriticalSection);
  21.     property Progress: Integer read FProgress;
  22.     property Info: string read FInfo;
  23.   end;
  24.  
  25.   TForm1 = class(TForm)
  26.     Label1: TLabel;
  27.     ProgressBar1: TProgressBar;
  28.     Timer1: TTimer;
  29.     procedure FormCreate(Sender: TObject);
  30.     procedure FormDestroy(Sender: TObject);
  31.     procedure Timer1Timer(Sender: TObject);
  32.   private
  33.     FCriticalSection: TRTLCriticalSection;
  34.     FThread: TTestThread;
  35.     procedure ThreadTerminated(Sender: TObject);
  36.   end;
  37.  
  38. var
  39.   Form1: TForm1;
  40.  
  41. implementation
  42.  
  43. {$R *.lfm}
  44.  
  45. constructor TTestThread.Create(AMax: Integer; ACriticalSection: TRTLCriticalSection);
  46. begin
  47.   inherited Create(True);
  48.   FMax := AMax;
  49.   FCriticalSection := ACriticalSection;
  50. end;
  51.  
  52. procedure TTestThread.Execute;
  53. begin
  54.   while not Terminated and (FProgress <= FMax) do
  55.   begin
  56.     SpinWait(7000); // Hide some real work
  57.     if TryEnterCriticalsection(FCriticalSection) <> 0 then
  58.     try
  59.       Inc(FProgress);
  60.       FmtStr(FInfo, 'Pos: %d, Total: %.1f%%', [FProgress, FProgress * 100/ FMax]);
  61.     finally
  62.       LeaveCriticalsection(FCriticalSection);
  63.     end;
  64.   end;
  65. end;
  66.  
  67. procedure TForm1.ThreadTerminated(Sender: TObject);
  68. begin
  69.   FThread := nil;
  70.   ProgressBar1.Position := ProgressBar1.Max;
  71.   Label1.Caption := 'Complete!';
  72. end;
  73.  
  74. procedure TForm1.Timer1Timer(Sender: TObject);
  75. var
  76.   LPosition: Integer;
  77.   LCaption: string;
  78. begin
  79.   if Assigned(FThread) then
  80.   begin
  81.     EnterCriticalsection(FCriticalSection);
  82.     try
  83.       LPosition := FThread.Progress;
  84.       LCaption := FThread.Info;
  85.     finally
  86.       LeaveCriticalsection(FCriticalSection);
  87.     end;
  88.     ProgressBar1.Position := LPosition;
  89.     Label1.Caption := LCaption;
  90.   end
  91.   else
  92.     Timer1.Enabled := False;
  93. end;
  94.  
  95. procedure TForm1.FormCreate(Sender: TObject);
  96. begin
  97.   ProgressBar1.Max := 200000;
  98.   Timer1.Interval := 200;
  99.   InitCriticalSection(FCriticalSection);
  100.   FThread := TTestThread.Create(ProgressBar1.Max, FCriticalSection);
  101.   FThread.FreeOnTerminate := True;
  102.   FThread.OnTerminate := @ThreadTerminated;
  103.   FThread.Start;
  104. end;
  105.  
  106. procedure TForm1.FormDestroy(Sender: TObject);
  107. begin
  108.   if Assigned(FThread) then
  109.     FThread.OnTerminate := nil;
  110.   FThread.Free;
  111.   DoneCriticalsection(FCriticalSection);
  112. end;
  113.  
  114. end.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Thread Safety?
« Reply #62 on: February 23, 2018, 01:29:25 pm »
Oh welll...<It's not>
Specialize a type, not a var.

knuckles

  • Full Member
  • ***
  • Posts: 122
Re: Thread Safety?
« Reply #63 on: February 23, 2018, 08:22:23 pm »
I must admit I didn't envisage this to become as complex or discussed as it turned out to be.

As a beginner regarding multi threading and parallel programming it seems there is so much more for me to read and learn about, so again thanks to everyone who replied and especially posted examples etc, it gives me lots of information to take in and a starting place to work from and hopefully begin to understand more clearly.

 

TinyPortal © 2005-2018