Recent

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

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Thread Safety?
« Reply #15 on: February 21, 2018, 07:32:21 am »
Or use synchronizing objects. Example:
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.     FSync: IReadWriteSync;
  17.   protected
  18.     procedure Execute; override;
  19.   public
  20.     constructor Create(AMax: Integer; const ASync: IReadWriteSync);
  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.     FSync: IReadWriteSync;
  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; const ASync: IReadWriteSync);
  46. begin
  47.   inherited Create(False);
  48.   FMax := AMax;
  49.   FSync := ASync;
  50. end;
  51.  
  52. procedure TTestThread.Execute;
  53. begin
  54.   while not Terminated and (FProgress <= FMax) do
  55.   begin
  56.     Sleep(10); // Some long work
  57.     FSync.BeginWrite;
  58.     try
  59.       Inc(FProgress);
  60.       FInfo := Format('Pos: %d, Total: %.1f%%', [FProgress, FProgress * 100/ FMax]);
  61.     finally
  62.       FSync.EndWrite;
  63.     end;
  64.   end;
  65. end;
  66.  
  67. procedure TForm1.ThreadTerminated(Sender: TObject);
  68. begin
  69.   FreeAndNil(FThread);
  70.   ProgressBar1.Position := ProgressBar1.Max;
  71.   Label1.Caption := 'Complete!';
  72. end;
  73.  
  74. procedure TForm1.Timer1Timer(Sender: TObject);
  75. begin
  76.   if Assigned(FThread) then
  77.   begin
  78.     FSync.BeginRead;
  79.     try
  80.       ProgressBar1.Position := FThread.Progress;
  81.       Label1.Caption := FThread.Info;
  82.     finally
  83.       FSync.EndRead;
  84.     end;
  85.   end;
  86. end;
  87.  
  88. procedure TForm1.FormCreate(Sender: TObject);
  89. begin
  90.   ProgressBar1.Max := 2000;
  91.   FSync := TSimpleRWSync.Create as IReadWriteSync;
  92.   FThread := TTestThread.Create(ProgressBar1.Max, FSync);
  93.   FThread.OnTerminate := @ThreadTerminated;
  94. end;
  95.  
  96. procedure TForm1.FormDestroy(Sender: TObject);
  97. begin
  98.   if Assigned(FThread) then
  99.   begin
  100.     FThread.OnTerminate := nil; // Exclude GUI
  101.     FThread.Terminate; // Signal to stop
  102.     FThread.WaitFor;
  103.     FThread.Free;
  104.   end;
  105. end;
  106.  
  107. end.

zoltanleo

  • Sr. Member
  • ****
  • Posts: 488
Re: Thread Safety?
« Reply #16 on: February 21, 2018, 07:50:08 am »
no processmessages is needed.
Thank you very much for explanations. It is very informatively. I think that not only I, but also many other people would be grateful to you if you would lay out the minimum demo-project here.  :)
Win10 LTSC x64/Deb 11 amd64(gtk2/qt5)/Darwin Cocoa (Monterey):
Lazarus x32/x64 2.3(trunk); FPC 3.3.1 (trunk), FireBird 3.0.10; IBX by TonyW

Sorry for my bad English, I'm using translator ;)

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Thread Safety?
« Reply #17 on: February 21, 2018, 10:01:40 am »
I think that not only I, but also many other people would be grateful to you if you would lay out the minimum demo-project here.  :)
I took an empty project, added ProgressBar and Timer. Formed events OnCreate, OnDestroy and Timer.OnTimer. Source code is provided earlier.

knuckles

  • Full Member
  • ***
  • Posts: 122
Re: Thread Safety?
« Reply #18 on: February 21, 2018, 01:01:38 pm »
Yes I have also heard a lot of bad things about Application.ProcessMessages,

I will take more time to read these follow up messages and examples. I was going to attempt something today but glancing at what has been posted I think I would have ended off track, so thanks again everyone for your input I will study what has been posted and play around some more :)

The main thing I was interested in was ensuring I made my threads safe and don't violate coding standards etc, of course seeing examples helps a lot to know what the syntax and things look like too.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Thread Safety?
« Reply #19 on: February 21, 2018, 01:38:51 pm »
@ASerge

I tried your code on linux (under debugger), and if the timer interval is set low enough (~30 or below) the progressbar does reach its Max.
However, the form remains unresponsive, and the app has to be killed.
Have I missed a vital piece?

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: Thread Safety?
« Reply #20 on: February 21, 2018, 02:11:03 pm »
Code of ASerge works fine for me on Windows.
Attached a compilable test-project.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Thread Safety?
« Reply #21 on: February 21, 2018, 02:59:51 pm »
@ASerge
I tried your code on linux (under debugger), and if the timer interval is set low enough (~30 or below) the progressbar does reach its Max.
However, the form remains unresponsive, and the app has to be killed.
I don't know. Have windows only.
Try to replace
Code: Pascal  [Select][+][-]
  1. Label1.Caption := FThread.Info;
with
Code: Pascal  [Select][+][-]
  1. S := FThread.Info; Label1.Caption := S;
Maybe the library takes a long time to transfer the string to the control, and the thread has already been freed?
Or replace
Code: Pascal  [Select][+][-]
  1. Label1.Caption :=
with
Code: Pascal  [Select][+][-]
  1. Caption :=

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Thread Safety?
« Reply #22 on: February 21, 2018, 06:02:48 pm »
Unfortunately neither of your suggestions made any difference, either to my implementation of your code, or to rvk's attached project (which is virtually identical to mine), when run under linux.

I can only think there is some incompatibility with IReadWriteSync and "uses cthreads".

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: Thread Safety?
« Reply #23 on: February 21, 2018, 06:40:10 pm »
Howard, yes. I have installed Mint 18.3 in a VM and I see the same.
Although the form is responsive, the progress bar advances with great leaps and the program hangs at the end.

Why is UseCThreads not default defined for Linux? I commented out the IFDEF now but I thought this should be default.
Code: Pascal  [Select][+][-]
  1. uses
  2.   {$IFDEF UNIX}{.$IFDEF UseCThreads}
  3.   cthreads,
  4.   {.$ENDIF}{$ENDIF}
  5.  

For the great leaps the ProgressBar1.Smooth should be set to true.
After that the TTimer.Inverval can be set to a lower frequency, like 100.
The hanging at the end is due to the FreeAndNil(FThread).
I thought you should NEVER EVER free something in it's own even-handler (which is done here).
Comment it out and all works fine:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ThreadTerminated(Sender: TObject);
  2. begin
  3.   // FreeAndNil(FThread);
  4.   ProgressBar1.Position := ProgressBar1.Max;
  5.   Label1.Caption := 'Complete!';
  6. end;

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Thread Safety?
« Reply #24 on: February 21, 2018, 07:24:04 pm »
no processmessages is needed.
Thank you very much for explanations. It is very informatively. I think that not only I, but also many other people would be grateful to you if you would lay out the minimum demo-project here.  :)
OK here you go a minimalistick sample of multihreading with out processmessages simple and to the point. I have added a couple of try/excepts/finally that showcase a more stable execution path and moved the thread code to a secondary method for simplicity's shake alone.
« Last Edit: February 21, 2018, 07:34:19 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

JD

  • Hero Member
  • *****
  • Posts: 1848
Re: Thread Safety?
« Reply #25 on: February 21, 2018, 07:28:40 pm »
@ASerge

I tried your code on linux (under debugger), and if the timer interval is set low enough (~30 or below) the progressbar does reach its Max.
However, the form remains unresponsive, and the app has to be killed.
Have I missed a vital piece?

I can confirm this also. It stops at 98.2%.
Windows - Lazarus 2.1/FPC 3.2 (built using fpcupdeluxe),
Linux Mint - Lazarus 2.1/FPC 3.2 (built using fpcupdeluxe)

mORMot; Zeos 8; SQLite, PostgreSQL & MariaDB; VirtualTreeView

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Thread Safety?
« Reply #26 on: February 21, 2018, 07:30:23 pm »
for simplicity's shake alone.

That must be elegant.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Thread Safety?
« Reply #27 on: February 21, 2018, 07:34:49 pm »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

rvk

  • Hero Member
  • *****
  • Posts: 6163
Re: Thread Safety?
« Reply #28 on: February 21, 2018, 07:39:55 pm »
@ASerge
I tried your code on linux (under debugger), and if the timer interval is set low enough (~30 or below) the progressbar does reach its Max.
However, the form remains unresponsive, and the app has to be killed.
Have I missed a vital piece?
I can confirm this also. It stops at 98.2%.
I already solved the hanging at the end.
See my post earlier.

(FreeAndNil(FThread); should never be called in its own event-handler. And FThread.OnTerminate is an event-handler.)

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Thread Safety?
« Reply #29 on: February 21, 2018, 07:41:09 pm »
@ASerge
Thanks for the sample.
  • Threads should not be terminated inside their events (Thanks rvk)
  • Using TSimpleRWSync/CriticalSection means threads might pause waiting for other threads. Not needed here.
  • Calling Terminate and WaitFor is redundant as TThread.Free leads to calling both, AFAIR.

 

TinyPortal © 2005-2018