Home > other >  Delphi multiThreading
Delphi multiThreading

Time:08-08

I'm new at SO, so forgive me if my question isn't in the right place or been answered before. The questions is about multi-threading with Delphi 10.4. I'm getting Access Violation error on my app, here is a very simple example:

type
  myThread = class(TThread)
  protected
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    mySideTask : myThread;

  public
    { Public declarations }
  end;
  
var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  with mySideTask.Create do
    FreeOnTerminate:=True
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if mySideTask<>nil then
  begin
    mySideTask.Terminate;
    mySideTask.WaitFor;
    FreeAndNil(mySideTask);
  end;
end;

{ myThread }

procedure myThread.Execute;
begin
  Synchronize(
    procedure
    begin
        Form1.Memo1.Lines.Add('running my side task')
    end);
end;

No error if I don't create an instance of the thread (which is confusing me):

procedure TForm1.Button1Click(Sender: TObject);
begin
  myThread.Create
end;

Can you please let me know what am I missing.

CodePudding user response:

The code in Button1Click() is wrong. You are calling Create() as an instance method on your mySideTask variable, but it is not pointing at a valid object instance.

Try this instead:

procedure TForm1.Button1Click(Sender: TObject);
begin
  mySideTask := myThread.Create(False{True});
  //mySideTask.FreeOnTerminate := True;
  //mySideTask.Start;
end;

Notice I commented out the handling of FreeOnTerminate=True. The reason for that is because that setting is meant for create-and-forget type of threads. The thread will destroy itself after its Execute() method exits. So it is not safe to call WaitFor() or Free() on a thread that could destroy itself at any moment.

If you want to use FreeOnTerminate=True, then the code should look more like this instead:

type
  myThread = class(TThread)
  protected
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    mySideTask : myThread;
    procedure SideTaskTerminated(Sender: TObject);
  public
    { Public declarations }
  end;
  
var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  mySideTask := myThread.Create(True);
  mySideTask.FreeOnTerminate := True;
  mySideTask.OnTerminated := SideTaskTerminated;
  mySideTask.Start;
end;

procedure TForm1.SideTaskTerminated(Sender: TObject);
begin
  mySideTask := nil;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if mySideTask <> nil then
  begin
    mySideTask.FreeOnTerminate := False;
    mySideTask.Terminate;
    mySideTask.WaitFor;
    FreeAndNil(mySideTask);
  end;
end;

{ myThread }

procedure myThread.Execute;
begin
  Synchronize(
    procedure
    begin
        Form1.Memo1.Lines.Add('running my side task')
    end);
end;
  • Related