该应用程序是用Delphi XE编写的。
我有两个类,TBoss和TWorker,它们都是基于TThread。 TBoss是一个单实例线程,启动后会创建大约20个TWorker线程。
当老板创建TWorker实例时,它会为其分配一个调用同步的方法,当Worker完成正在执行的操作时,调用此方法允许Boss访问Worker上的记录。
但是我觉得这是一个问题,调用synchronize似乎是锁定整个应用程序 - 阻塞主(ui)线程。真的应该只是将该工作者与老板线程同步......
以前我使用消息/打包记录在线程之间发送内容,这些内容运行良好。然而,这样做更清洁,更好......只是非常阻塞。
有没有办法在worker中调用Syncronize只等待Boss线程?
我的代码:
type
TWorker = class(TThread)
private
fResult : TResultRecord;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
....
end;
...
...
procedure TWorker.SendBossResults;
begin
if (Terminated = False) then
begin
Synchronize(SendResult);
end;
end;
procedure TWorker.SendResult;
begin
if (Terminated = false) and Assigned(FOnSendResult) then
begin
FOnSendResult(Self);
end;
end;
然后在我的Boss线程中,我会做这样的事情
var
Worker : TWorker;
begin
Worker := TWorker.Create;
Worker.OnTerminate := OnWorkerThreadTerminate;
Worker.OnSendResult := ProcessWorkerResults;
所以我的老板会有一个名为ProcessWorkerResults的方法 - 这就是在Synchronize(SendResult)上运行的方法。工人。
procedure TBoss.ProcessWorkerResults(Sender: TObject);
begin
if terminated = false then
begin
If TWorker(Sender).Result.HasRecord then
begin
fResults.Add(TWorker(Sender).Result.Items);
end;
end;
end;
答案 0 :(得分:10)
Synchronize
专门用于执行主主题中的代码;这就是它似乎锁定一切的原因。
您可以使用多种方式从工作线程到老板线程进行通信:
为每个工作线程添加一个回调, 并从老板线程中分配它 当它被创建时。它可以传回来 无论作为参数,还是a 线程ID或其他一些标识符。
从工作线程发布消息 给老板线程使用 PostThreadMessage。该 这里的劣势就是老板 线程必须有一个窗口句柄 (见Classes.AllocateHWnd) 德尔福帮助和David Heffernan的评论如下)。
使用优质的第三方 线程库。看到 OmniThreadLibrary - 它是免费的, 操作系统,写得非常好。
我的选择将是第三个。 Primoz为你完成了所有艰苦的工作。 :)
在你的评论之后,这是我的第一个建议。请注意,这是未经测试,因为编写TBoss和TWorker线程的代码+测试应用程序对于我这一刻的时间来说有点长......应该足够给你了要点,我希望。
type
TWorker = class(TThread)
private
fResult : TResultRecord;
fListIndex: Integer;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
property ListIndex: Integer read FListIndex write FListIndex;
....
end;
type
TBoss=class(TThread)
private
FWorkerList: TThreadList; // Create in TBoss.Create, free in TBoss.Free
...
end;
procedure TWorker.SendBossResults;
begin
if not Terminated then
SendResult;
end;
procedure TBoss.ProcessWorkerResults(Sender: TObject);
var
i: Integer;
begin
if not terminated then
begin
If TWorker(Sender).Result.HasRecord then
begin
FWorkerList.LockList;
try
i := TWorker(Sender).ListIndex;
// Update the appropriate record in the WorkerList
TResultRecord(FWorkerList[i]).Whatever...
finally
FWorkerList.UnlockList;
end;
end;
end;
end;
答案 1 :(得分:8)
您可以使用线程安全队列。在DelphiXE中有TThreadedQueue。如果您没有DXE,请尝试使用OmniThreadLibray - 这个库非常适合所有线程问题。
答案 2 :(得分:1)
正如我在Delphi 2009及更高版本中提到的新选项,这里有一个指向线程之间生产者/消费者通信的示例的链接,基于新的objct锁,在我的博客中:
Thread Synchronization with Guarded Blocks in Delphi
在note regarding the deprecated methods TThread.Suspend和 TThread.Resume,德尔福的Embarcadero DocWiki 建议“线程 同步技术应该是 基于SyncObjs.TEvent和 SyncObjs.TMutex。“但是, 另一个同步类 自Delphi 2009以来可用:TMonitor。 它使用已经存在的对象锁 在这个版本中介绍......
答案 3 :(得分:0)
public
类的 TWorker
属性必须具有get
和set
方法,因此您可以使用Tcriticalsection
来提供属性的值。否则,您将遇到线程安全问题。您的示例似乎没问题,但在现实世界中,数千个线程访问相同的值会导致读取错误。使用关键部分..你不必使用任何同步。这样您就可以避免转到窗口的消息队列并提高性能。此外,如果您在Windows服务应用程序中使用此代码(不允许使用Windows消息),此示例将不起作用。除非可以访问Windows消息队列,否则synchronize方法不起作用。
答案 4 :(得分:0)
解决!! (从问题中回答)
这个问题的解决方案是两次折叠
首先删除TWorker SendBossResult方法中的同步调用。
第二步将一个fProcessWorkerResult CritialSection添加到TBoss类。在TBoss的创建/销毁中创建并释放它。在ProcessWorkerResults方法中,调用fProcessWorkerResult.Enter和fProcessWorkerResult.leave围绕代码,这些代码需要安全地从多个工作流结果流入。
以上是Kens代码和后续评论后的结论。非常感谢亲切的先生,给你帽子!。