Forum > General
TThread in an array - freezes on closing
Slawek:
The next problem with threads I see is that I wanted to create an array of threads. Run them all together and finish with the button or close the form.
When the array is a single element: array[0..0], everything works. However with 2 elements: array[0..1] everything hangs when I click btStop or close the form. I don't understand why the program crashes.
Second case. I send ThreadID and GetCurrentThreadId to ListBox(lbThreads). ThreadID is different for each thread while GetCurrentThreadId is the same. Does GetCurrentThreadId show the id of the main thread in this situation?
The whole code looks like this:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls; type TShowStatusEvent = procedure(Id, ThId: Integer; Status, Task: String) of Object; { TMyThread } TMyThread = class(TThread) private FId: Integer; fStatusText : string; fTaskText : string; FOnShowStatus: TShowStatusEvent; procedure SetId(AValue: Integer); procedure ShowStatus; protected procedure Execute; override; public constructor Create(CreateSuspended : boolean); property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus; property Id: Integer read FId write SetId; end; { TForm1 } TForm1 = class(TForm) btStart: TButton; btStop: TButton; laTask: TLabel; laStatus: TLabel; lbThreads: TListBox; StatusBar1: TStatusBar; procedure btStartClick(Sender: TObject); procedure btStopClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private procedure ShowStatus(Id, ThId: Integer; Status, Task: string); procedure ThreadTerminated(Sender: TObject); public end; var Form1: TForm1; aThreads: array[0..1] of TMyThread; implementation {$R *.lfm} { TMyThread } procedure TMyThread.ShowStatus;begin if Assigned(FOnShowStatus) then begin FOnShowStatus(FId, ThreadID, fStatusText, fTaskText); end;end; procedure TMyThread.SetId(AValue: Integer);begin if FId=AValue then Exit; FId:=AValue;end; procedure TMyThread.Execute;var i: Integer;begin fStatusText := 'Starting...'; Synchronize(@ShowStatus); fStatusText := 'Running...'; while (not Terminated) do begin //repeat all tasks for i:= 1 to 9 do begin if Terminated then Break; //abort fTaskText:='Task no '+i.ToString; sleep(500); if not Terminated then Synchronize(@Showstatus); end; end;end; constructor TMyThread.Create(CreateSuspended: boolean);begin inherited Create(CreateSuspended);end; { TForm1 } procedure TForm1.btStartClick(Sender: TObject);var i: Integer;begin btStart.Enabled:=False; lbThreads.Clear; for i:= Low(aThreads) to High(aThreads) do lbThreads.Items.Add('.'); for i:= Low(aThreads) to High(aThreads) do begin aThreads[i] := TMyThread.Create(true); aThreads[i].FreeOnTerminate := True; aThreads[i].OnShowStatus := @ShowStatus; aThreads[i].OnTerminate := @ThreadTerminated; aThreads[i].Id:=i; laStatus.Caption:='Started'; aThreads[i].Start; end; btStop.Enabled:=True;end; procedure TForm1.btStopClick(Sender: TObject);var i: Integer;begin btStop.Enabled:=False; for i:= Low(aThreads) to High(aThreads) do begin aThreads[i].Terminate; aThreads[i].WaitFor; end; laStatus.Caption:='Terminated'; btStart.Enabled:=True; if Assigned(aThreads[0]) then ShowMessage('Assigned!');end; procedure TForm1.FormCreate(Sender: TObject);begin btStop.Enabled:=False;end; procedure TForm1.FormDestroy(Sender: TObject);var i: Integer;begin for i:= Low(aThreads) to High(aThreads) do begin if Assigned(aThreads[i]) then begin aThreads[i].Terminate; aThreads[i].WaitFor; end; end;end; procedure TForm1.ShowStatus(Id, ThId: Integer; Status, Task: string);const TXT='%S(%D/%D/%D)=%S';var cThId: Integer;begin laStatus.Caption := Status; laTask.Caption := Task; cThId := GetCurrentThreadId; lbThreads.Items[Id] := Format(TXT, [Status, Id, ThId, cThId, Task]);end; procedure TForm1.ThreadTerminated(Sender: TObject);begin if Sender is TMyThread then aThreads[(Sender as TMyThread).Id] := nil;end; end.
jamie:
if you do a Waitfor on a thread that never got started or was placed in suspend mode it may hang there at the Waitfor.
Don't use Waitfor unless you know the thread is actually running.
This could be one of those little bugs we don't like to talk about.! Oh No, Say it's not so!
Slawek:
Hello jamie,
--- Quote from: jamie on February 09, 2023, 02:36:07 am ---if you do a Waitfor on a thread that never got started or was placed in suspend mode it may hang there at the Waitfor.
Don't use Waitfor unless you know the thread is actually running.
--- End quote ---
Thank you for your comments.
I deleted both Waitfors and now there is indeed no freeze.
1.
But shouldn't I wait for all threads to finish when closing the application and/or form?
2.
I don't understand this. If I tell you to terminate threads (aThreads.Terminate), then why aThreads.WaitFor; freezes? So how can I make sure threads are actually terminated here?
--- Quote from: jamie on February 09, 2023, 02:36:07 am ---This could be one of those little bugs we don't like to talk about.! Oh No, Say it's not so!
--- End quote ---
I like to talk about all the bugs, because only then will I be able to write correct code.
It annoys me when I don't understand something and make stupid mistakes.
Warfley:
For this purpose there exist a special synchronization datastructure called a barrier: https://en.m.wikipedia.org/wiki/Barrier_(computer_science)
At its core it works like this, a barrier requires a certain number of threads to break through. A thread that runs into the barrier waits until enough threads are waiting and then all of them will continue at once.
I always think of it as a race track, where everyone waits at the start line until all are in place and then the race starts.
In your program you can use a barrier to wait for the threads. When starting the threads you setup the barrier with a threshold of n+1 (where n is the number of threads). In the on terminate of your threads you let them enter the barrier. When waiting for all threads your main thread enters the barrier as well. As soon as all the threads are finished, and the main thread is also at the point of entering the barrier, the barrier releases, all the threads that are waiting will continue with their FreeOnTerminate, and the main thread will continue execution knowing that all other threads just finished
alpine:
@Slawek
You are making the same mistake I wrote about:
https://forum.lazarus.freepascal.org/index.php/topic,62182.msg470478.html#msg470478
Just comment the:
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- aThreads[i].FreeOnTerminate := True; in the btStartClick and you'll see the difference.
You're calling WaitFor on a dangling reference!
Navigation
[0] Message Index
[#] Next page