Recent

Author Topic: TThread - Why can't I start a thread again?  (Read 1052 times)

Slawek

  • New Member
  • *
  • Posts: 42
TThread - Why can't I start a thread again?
« on: February 07, 2023, 11:29:08 pm »
Hello,

I made such an example:
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. type
  11.   TShowStatusEvent = procedure(Status, Task: String) of Object;
  12.  
  13.   { TMyThread }
  14.  
  15.   TMyThread = class(TThread)
  16.   private
  17.     fStatusText : string;
  18.     fTaskText : string;
  19.     FOnShowStatus: TShowStatusEvent;
  20.     procedure ShowStatus;
  21.   protected
  22.     procedure Execute; override;
  23.   public
  24.     constructor Create(CreateSuspended : boolean);
  25.     property OnShowStatus: TShowStatusEvent read FOnShowStatus write FOnShowStatus;
  26.   end;
  27.  
  28.   { TForm1 }
  29.  
  30.   TForm1 = class(TForm)
  31.     btStart: TButton;
  32.     btStop: TButton;
  33.     laTask: TLabel;
  34.     laStatus: TLabel;
  35.     procedure btStartClick(Sender: TObject);
  36.     procedure btStopClick(Sender: TObject);
  37.     procedure FormCreate(Sender: TObject);
  38.     procedure FormDestroy(Sender: TObject);
  39.   private
  40.     MyThread: TMyThread;
  41.     procedure ShowStatus(Status, Task: string);
  42.   public
  43.  
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.  
  49. implementation
  50.  
  51. {$R *.lfm}
  52.  
  53. { TMyThread }
  54.  
  55. procedure TMyThread.ShowStatus;
  56. begin
  57.   if Assigned(FOnShowStatus) then begin
  58.     FOnShowStatus(fStatusText, fTaskText);
  59.   end;
  60. end;
  61.  
  62. procedure TMyThread.Execute;
  63. var i: Integer;
  64. begin
  65.   fStatusText := 'TMyThread Starting...';
  66.   Synchronize(@ShowStatus);
  67.   fStatusText := 'TMyThread Running...';
  68.   while (not Terminated) do begin //repeat all tasks
  69.     for i:= 1 to 9 do begin
  70.       if Terminated then Break; //abort
  71.       fTaskText:='Task no '+i.ToString;
  72.       sleep(500);
  73.       if not Terminated then
  74.         Synchronize(@Showstatus);
  75.     end;
  76.   end;
  77. end;
  78.  
  79. constructor TMyThread.Create(CreateSuspended: boolean);
  80. begin
  81.   inherited Create(CreateSuspended);
  82. end;
  83.  
  84. { TForm1 }
  85.  
  86. procedure TForm1.btStartClick(Sender: TObject);
  87. begin
  88.   btStart.Enabled:=False;
  89.   laStatus.Caption:='Started';
  90.   MyThread.Start;
  91.   btStop.Enabled:=True;
  92. end;
  93.  
  94. procedure TForm1.btStopClick(Sender: TObject);
  95. begin
  96.   btStop.Enabled:=False;
  97.   MyThread.Terminate;
  98.   MyThread.WaitFor;
  99.   laStatus.Caption:='Terminated';
  100.   btStart.Enabled:=True;
  101. end;
  102.  
  103. procedure TForm1.FormCreate(Sender: TObject);
  104. begin
  105.   btStop.Enabled:=False;
  106.   MyThread := TMyThread.Create(true);
  107.   MyThread.FreeOnTerminate := False;
  108.   MyThread.OnShowStatus := @ShowStatus;
  109. end;
  110.  
  111. procedure TForm1.FormDestroy(Sender: TObject);
  112. begin
  113.   if Assigned(MyThread) then begin
  114.     MyThread.Terminate;
  115.     MyThread.WaitFor;
  116.   end;
  117. end;
  118.  
  119. procedure TForm1.ShowStatus(Status, Task: string);
  120. begin
  121.   laStatus.Caption := Status;
  122.   laTask.Caption := Task;
  123. end;
  124.  
  125. end.
  126.  

I can start and stop it, but when I want to start it again, the tasks in the thread are not executed. Even "TMyThread Starting..." is not displayed.
Should the thread stop be done differently in this example? Of course, I'm not talking about a pause. I want to stop the thread completely and then start it again. Unfortunately, I don't know why it doesn't start again.

If I move the thread creation (marked fragment) to btStartClick then everything works. Can a created thread be run only once?

Code: Pascal  [Select][+][-]
  1. procedure TForm1.btStartClick(Sender: TObject);
  2. begin
  3.   btStart.Enabled:=False;
  4.   MyThread := TMyThread.Create(true);
  5.   MyThread.FreeOnTerminate := True;
  6.   MyThread.OnShowStatus := @ShowStatus;
  7.   laStatus.Caption:='Started';
  8.   MyThread.Start;
  9.   btStop.Enabled:=True;
  10. end;

cdbc

  • Hero Member
  • *****
  • Posts: 995
    • http://www.cdbc.dk
Re: TThread - Why can't I start a thread again?
« Reply #1 on: February 08, 2023, 01:36:08 pm »
Hi
Quote
Can a created thread be run only once?
Yes. When you exit the Execute method, the thread is done. I.e. you must create it anew. Try to follow TThread in the source, how it gets created etc...
Then you'll see that, Execute IS the ThreadFunc. TThread just adds a lot of clever stuff to the mix  ;)
Hth - Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Slawek

  • New Member
  • *
  • Posts: 42
Re: TThread - Why can't I start a thread again?
« Reply #2 on: February 08, 2023, 02:06:44 pm »
Hi
Quote
Can a created thread be run only once?
Yes. When you exit the Execute method, the thread is done. I.e. you must create it anew.
That explains a lot to me. Thank you for your response.

Try to follow TThread in the source, how it gets created etc...
Then you'll see that, Execute IS the ThreadFunc.
I can't find it, but I believe you it is.

cdbc

  • Hero Member
  • *****
  • Posts: 995
    • http://www.cdbc.dk
Re: TThread - Why can't I start a thread again?
« Reply #3 on: February 08, 2023, 07:54:08 pm »
Hi Slawek
In the source editor, [ctrl + left-click] on TThread, editor will jump to its implementation, then [ctrl + shift + down arrow] will jump from interface to implementation... Try to follow TThread.Create(...), then you'll see "SysCreate(CreateSuspended, StackSize);" at the end, ctrl-click on that and then ctrl-shift-down... There's some pretty clever stuff in there  ;)
If you must, you can google for "sleeping threads", OmniThreadLibrary https://github.com/gabr42/OmniThreadLibrary implements them and I think EZThreads does too... https://github.com/mr-highball/ezthreads
... or you can roll your own  :D
Have fun - Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

Slawek

  • New Member
  • *
  • Posts: 42
Re: TThread - Why can't I start a thread again?
« Reply #4 on: February 08, 2023, 10:32:04 pm »
Thank you very much for this information and links. I'm just getting started with threads. This is not an easy topic. I need to calmly analyze, test and sort everything out in my head  :)
There is quite a lot of knowledge about threads on the internet and on this forum, but so far it seems chaotic to me.
« Last Edit: February 08, 2023, 10:33:45 pm by Slawek »

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: TThread - Why can't I start a thread again?
« Reply #5 on: February 09, 2023, 12:54:02 am »
Maybe it makes no difference here however, you did not specify to OVERRIDE the constructor in the base thread class.


Constructor Create(.....) override; <<<<

Scrub that!


« Last Edit: February 09, 2023, 01:15:12 am by jamie »
The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 6077
Re: TThread - Why can't I start a thread again?
« Reply #6 on: February 09, 2023, 01:31:27 am »
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, Grids;
  9.  
  10. type
  11.  TMyThread = Class(TTHread)
  12.    Constructor Create(CreateSuspended:Boolean;Const StackSize:SizeUint=DefaultStackSize);Virtual;
  13.    Procedure Execute; Override;
  14.  end;
  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.     procedure FormCreate(Sender: TObject);
  24.     procedure FormDestroy(Sender: TObject);
  25.   private
  26.  
  27.   public
  28.     MyTh:TMyThread;
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37. Procedure TMyThread.Execute;
  38. Var
  39.   T,U:Int64;
  40. Begin
  41.   T:=0;
  42.   While Not Terminated do
  43.     Begin
  44.      U:= GetTickCount64;
  45.      if Abs(U-T) > 2000 THen
  46.        Begin
  47.          T := U;
  48.          Beep;
  49.        end;
  50.     end;
  51. end;
  52.  
  53. Constructor TMyThread.Create(CreateSuspended:Boolean;Const StackSize:SizeUint=DefaultStackSize);
  54. Begin
  55.   inherited Create(CreateSuspended,StackSize);
  56. end;
  57.  
  58. { TForm1 }
  59.  
  60. procedure TForm1.Button1Click(Sender: TObject);
  61. begin
  62.  MyTh.Start;
  63. end;
  64.  
  65. procedure TForm1.Button2Click(Sender: TObject);
  66. begin
  67.  Myth.Suspend;
  68. end;
  69.  
  70. procedure TForm1.FormCreate(Sender: TObject);
  71. begin
  72.   MyTh := TMythread.Create(True);
  73.   MyTh.FreeOnTerminate := False;
  74. end;
  75.  
  76. procedure TForm1.FormDestroy(Sender: TObject);
  77. begin
  78.   MyTh.Terminate;
  79.   MyTh.Free;
  80. end;
  81.  
  82. end.
  83.  
  84.  

This works for me.

You can also do this in the Execute Method by using a flag.
Code: Pascal  [Select][+][-]
  1. Procedure TMyThread.Execute;
  2. Var
  3.   T,U:Int64;
  4. Begin
  5.   T:=0;
  6.   While Not Terminated do
  7.    If Not Paused Then
  8.     Begin
  9.      U:= GetTickCount64;
  10.      if Abs(U-T) > 2000 THen
  11.        Begin
  12.          T := U;
  13.          Beep;
  14.        end;
  15.     end;
  16. end;                    
  17.  

So you get the thread started and simply toggle the PAUSE boolean which is something you add to your Thread class in the public section.

 This way the thread is always running and you can also yield to other threads using that check point..

  If Not Terminated Then
     If Not Paused then
          Begin
             .....
          End Else Yield;

etc..
« Last Edit: February 09, 2023, 02:17:51 am by jamie »
The only true wisdom is knowing you know nothing

Slawek

  • New Member
  • *
  • Posts: 42
Re: TThread - Why can't I start a thread again?
« Reply #7 on: February 09, 2023, 04:21:37 pm »
Thank you Jamie. Your explanation of this example seems understandable to me. I'll test it tonight  :)

 

TinyPortal © 2005-2018