delphi TEvent.SetEvent上的TThread行为

xt0899hw  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(350)

当使用TThread和TEvent启动线程完成的工作时,我遇到了奇怪的行为。由于suspend/resume已被弃用,因此我正在寻找使用TEvent,因此这里只是我的简化代码,但它仍然不会按预期执行。我有TSampleThread类和FThreadStartEvent。例如,我创建了3个TSampleThread示例,但当我通过设置FThreadStartEvent启动一个线程时,不知何故,所有示例都被执行了。

问题是什么?谢谢大家。

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.
2guxujil

2guxujil1#

在这种情况下,不要为TEvent对象指定名称:
https://docwiki.embarcadero.com/Libraries/en/System.SyncObjs.TEvent.Create
设置名称为新事件对象提供名称或指定现有命名事件对象。如果没有其他线程或进程需要访问事件对象来等待其信号,则Name可以留空。名称可以是最多260个字符的字符串,不包括反斜杠字符()。如果使用Name指定现有事件对象,则在区分大小写的比较中,该值必须与现有事件的名称匹配。如果Name与现有信号量、互斥体或文件Map对象的名称匹配,则创建TEvent对象时,Handle设置为0,所有方法调用都将失败。
所有的TEvent对象都被分配了相同的名称,这导致所有的线程在Windows内核中共享一个 * 单个 * 命名的事件对象,从而通知 * 任何 * 线程将满足 * 所有 * 线程的等待。
此外,如果另一个应用中的线程决定访问相同的命名事件对象,则命名事件会使您的应用受到外部干扰。
你需要改变这一点:
FThreadStartEvent:= TEvent.Create(nil, true, false, 'ThreadStartEvent');
改为:
FThreadStartEvent := TEvent.Create(nil, true, false, '');
或者更简单:
FThreadStartEvent := TEvent.Create;
注意:你没有说你使用的是哪个版本的 Delphi 。您的代码在uses子句中使用了单元作用域名称,因此它必须至少为XE2。但是,在XE8之前,TEvent构造函数中有a bug,这导致它仍然创建一个 named 事件对象,即使Name参数为空。这个问题在XE8中得到了解决。对于早期版本,请直接使用Win32 CreateEvent()函数。

相关问题