I am facing strange behavior when using TThread and TEvent to start a work done by the thread. Since suspend/resume are deprecated, I was looking for using TEvent, so here is just a simplified code I have, but still it won't perform as expected. I have TSampleThread class with FThreadStartEvent. For example I create 3 instances of TSampleThread but when I start one thread by setting FThreadStartEvent in it, somehow all of the instances get executed.
What is the issue? Thanks everyone.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Contnrs, System.SyncObjs,
Vcl.StdCtrls;
type
TSampleThread = class;
TTaskProc = procedure(const SampleThread:TSampleThread) of object;
TSampleThread = class (TTHread)
public
FId:String;
FThreadStartEvent: TEvent;
FOnThreadExecute:TTaskProc;
constructor Create;
destructor Destroy; override;
procedure Execute; override;
procedure Start;
end;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
task1, task2, task3:TSampleThread;
procedure onTask(const SampleThread: TSampleThread);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TSampleThread.Execute;
begin
repeat
FThreadStartEvent.WaitFor(INFINITE);
FThreadStartEvent.ResetEvent;
if Assigned(TMethod(FOnThreadExecute).Code) then FOnThreadExecute(Self);
until terminated;
end;
procedure TSampleThread.Start;
begin
FThreadStartEvent.SetEvent;
end;
constructor TSampleThread.Create;
begin
FreeOnTerminate:= False;
FOnThreadExecute:= nil;
FThreadStartEvent:= TEvent.Create(nil, true, false, 'ThreadStartEvent');
inherited Create(false);
end;
destructor TSampleThread.Destroy;
begin
FThreadStartEvent.SetEvent;
FThreadStartEvent.Free;
inherited;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
task1.Start;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
task1:= TSampleThread.Create;
task1.FId:= 'Task 1' ;
task1.FOnThreadExecute:= onTask;
task2:= TSampleThread.Create;
task2.FId:= 'Task 2' ;
task2.FOnThreadExecute:= onTask;
task3:= TSampleThread.Create;
task3.FId:= 'Task 3' ;
task3.FOnThreadExecute:= onTask;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
task1.Free;
task2.Free;
task3.Free;
end;
procedure TForm1.onTask(const SampleThread: TSampleThread);
begin
SampleThread.Synchronize(nil,
procedure
begin
Memo1.Lines.Add(SampleThread.FId);
end);
end;
end.

DO NOT assign a name to your
TEventobjects in this situation:https://docwiki.embarcadero.com/Libraries/en/System.SyncObjs.TEvent.Create
All of your
TEventobjects have the same name assigned, so that is causing all of your threads to share a single named event object in the Windows kernel, thus signaling any of your threads will satisfy the wait in all of your threads.Also, naming the event opens up your app to outside interference, if a thread in another app decides to access the same named event object.
You need to change this:
FThreadStartEvent:= TEvent.Create(nil, true, false, 'ThreadStartEvent');To this instead:
FThreadStartEvent := TEvent.Create(nil, true, false, '');Or simpler:
FThreadStartEvent := TEvent.Create;Note: you did not say which version of Delphi you are using. Your code is using Unit Scope names in the
usesclause, so it must be at least XE2. But, prior to XE8, there was a bug in theTEventconstructor that caused it to still create a named event object even when theNameparameter is blank. This was fixed in XE8. For earlier versions, use the Win32CreateEvent()function directly instead.