CreateEvent multiple listeners

272 Views Asked by At

I'm trying to receive an event in multiple instances of my application.

For that purpose I've created a small demo program. First my TWorkerThread:

unit WorkerThreadU;

interface

uses
  WinAPI.Windows, System.Classes;

type
  TOnUpdate = reference to procedure(const Value: Integer);

  TWorkerThread = class(TThread)
  private
    FUpdate: THandle;
    FValue: Integer;
    FResult: Integer;
    FUpdateReady: TOnUpdate;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Update;
    property Value: Integer read FValue write FValue;
    property OnUpdate: TOnUpdate read FUpdateReady write FUpdateReady;
  end;

implementation

{ TWorkerThread }

constructor TWorkerThread.Create;
begin
  inherited Create(False);
  FUpdate := CreateEvent(nil, False, False, '{B2DCFF9B-ABF7-49BA-8B7C-4F63EF20D99E}');
end;

destructor TWorkerThread.Destroy;
begin
  CloseHandle(FUpdate);
  inherited;
end;

procedure TWorkerThread.Execute;
begin
  while not Terminated do
  begin
    if WaitForSingleObject(FUpdate, 1000) <> WAIT_OBJECT_0 then
      continue;

    FResult := FValue * 2;

    if not Assigned(FUpdateReady) then
      continue;

    TThread.Queue(nil,
      procedure
      begin
        FUpdateReady(FResult);
      end);
  end;
end;

procedure TWorkerThread.Update;
begin
  SetEvent(FUpdate);
end;

end.

My form:

Form with large empty space, button and number edit

...and the source for it:

procedure TfrmEvents.FormCreate(Sender: TObject);
begin
  Caption := BoolToStr(Boolean(IsDebuggerPresent), True);
  FWorkerThread := TWorkerThread.Create;
  FWorkerThread.OnUpdate := procedure(const Value: Integer)
    begin
      Log(Format('2 * %d = %d', [inpValue.Value, Value]))
    end;
end;

procedure TfrmEvents.btnCalcClick(Sender: TObject);
begin
  try
    FWorkerThread.Value := inpValue.Value;
    Log('Calculating ...');
    FWorkerThread.Update;
  finally
  end;
end;

procedure TfrmEvents.Log(const msg: string);
begin
  lbLog.ItemIndex := lbLog.Items.Add(FormatDateTime('hh:nn:ss', Now) + ' ' + msg);
end;

My problem is that only one of the instances receives the event.

The program can also be found here.

1

There are 1 best solutions below

0
Uwe Raabe On BEST ANSWER

This probably happens because CreateEvent uses the same name for all thread instances. That way all threads use the same event. As the event is created with automatic reset, the first thread getting the event will reset it and the others aren't noticed anymore.

From the docs:

If this parameter is FALSE, the function creates an auto-reset event object, and system automatically resets the event state to nonsignaled after a single waiting thread has been released.