当使用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.
1条答案
按热度按时间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中得到了解决。对于早期版本,请直接使用Win32CreateEvent()
函数。