1

This is absolutely basic code for testing purpose. And I can't find out why after clicking the button the new thread is blocking the GUI ( Main thread). Is there any reason for this behavior to happen? I'm sorry for silly question, but I've spend hours on this basic thing and I'm a beginner with FPC.

{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type
  { TForm1 }
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1 : Tmemo;
    procedure Button1Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  end;

type
  TMemoThr = class(TThread)
      procedure Execute; override;
      Constructor Create(CreateSuspended : boolean);
  end;

  var
  Form1: TForm1;
  M :TMemoThr;

implementation
{$R *.lfm}
constructor TMemoThr.Create(CreateSuspended : boolean);
begin
  inherited Create(CreateSuspended);
  FreeOnTerminate := True;
end;

procedure TMemoThr.Execute();
begin
      while (not Terminated) do begin
            self.sleep(5000);  // this should only put thread to sleep, not entire Form
            showMessage('Inside');
      end;
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
     M := TMemoThr.Create(false);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
         M.Execute;
end;    
Gbr
  • 29
  • 6
  • Calling `showMessage` inside the threads `Execute` method is an extremely bad idea. If you remove it, you will find that the thread will continue executing. Better to use the debugger, and put a breakpoint where the `sleep` call is to verify that the thread continues to execute after each `sleep` – Dave Nottage Jul 12 '22 at 02:01
  • The `showMessage` doesn't really matter. The popup window pauses the thread until you close it - as expected. I've put it there, because if you remove it the program is just in constantly blocked state. The problem is that `sleep` inside TThread is blocking the whole GUI. – Gbr Jul 12 '22 at 02:12
  • 2
    I just realised you are explicitly calling `Execute` on the thread, which you should never do. Take that out, since the thread starts executing after you've created it. If you wish to defer starting the thread, the value for `CreateSuspended` should be `False`, and call `Start` on the thread when you want it to start – Dave Nottage Jul 12 '22 at 02:55
  • Thanks, it was exactly the solution to my problem ! Does it mean that explicit call to Execute() just run the MyThread.Execute() procedure in current thread, just like any other function ? – Gbr Jul 12 '22 at 03:15
  • 3
    Yes, you don't need to call ``Execute`` manually. It's called internally by the thread class! – Delphi Coder Jul 12 '22 at 05:02
  • Another problem is that you hold a reference to the thread in `M` and also set `FreeOnTerminate` to `True`. If you do the latter, you must not do the former. – David Heffernan Jul 12 '22 at 08:05
  • @DaveNottage "*the value for `CreateSuspended` should be `False`, and call `Start` on the thread when you want it to start*" - I think you meant "*`CreateSuspended` should be `TRUE`*" instead. – Remy Lebeau Jul 12 '22 at 16:53
  • @DavidHeffernan "*If you [set `FreeOnTerminate` to `True`], you must not [hold a reference to the thread in `M`]*" - you can hold the reference safely, if you assign an `OnTerminate` handler that clears the reference when the thread is finished. – Remy Lebeau Jul 12 '22 at 16:55
  • @Remy Lebeau Do you mean assigning instance variable to nil pointer at OnTerminate event ? `M := nil;` . What could go wrong if I don't do it ? – Gbr Jul 13 '22 at 06:32

1 Answers1

1

Here my Thread code:

  { TMainPortThread }
  TMainPortThread = class(TThread)
  private
    procedure Synchronous;
  protected
    procedure Execute; override;
  public
  end; 

Execute procedure:

procedure TMainPortThread.Execute;
var
  i:integer;
begin
  while true do begin
        // do your stuff
  end;
end; 

Start Thread:

procedure TForm1.Button2Click(Sender: TObject);
begin
     TMainPortThread.Create(false);
end;  
GeneCode
  • 7,545
  • 8
  • 50
  • 85