在线程之间同步/发送数据
该应用程序是用 Delphi XE 编写的。
我有两个类,TBoss 和 TWorker,它们都基于 TThread。 TBoss是一个单实例线程,它启动后会创建大约20个TWorker线程。
当 Boss 创建 TWorker 实例时,它会为其分配一个方法来调用同步,当 Worker 完成其正在执行的操作时,它会调用此方法,该方法允许 Boss 访问 Worker 上的记录。
然而我觉得这是一个问题,调用同步似乎锁定了整个应用程序 - 阻塞主(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;
然后在我的老板线程中我将执行类似的操作
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;
The app is written in Delphi XE.
I have two classes, a TBoss and TWorker, which are both based of of TThread.
The TBoss is a single instance thread, which starts up and then will create about 20 TWorker threads.
When the boss creates a instance of TWorker it assigns it a method to call synchronize on, when the Worker has finished with what it's doing it calls this method which allows the Boss to access a record on the Worker.
However I feel this is a problem, calling synchronize appears to be locking up the whole application - blocking the main (ui) thread. Really it should just be synchronizing that worker to the boss thread....
Previously I used messages/packed records to send content between threads which worked well. However doing it this way is much cleaner and nicer.... just very blocking.
Is there a way to call Syncronize in the worker to only wait for the Boss thread?
My code:
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;
Then in my Boss thread I will do something like this
var
Worker : TWorker;
begin
Worker := TWorker.Create;
Worker.OnTerminate := OnWorkerThreadTerminate;
Worker.OnSendResult := ProcessWorkerResults;
So my boss then has a method called ProcessWorkerResults - this is what gets run on the Synchronize(SendResult); of the worker.
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;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Synchronize
专门设计用于在main 线程中执行代码;这就是为什么它似乎锁定了一切。您可以使用多种方式从工作线程到老板线程进行通信:
向每个工作线程添加回调,
并从 boss 线程分配它
当它被创建时。可以传回来
无论作为参数,以及
线程 ID 或其他一些标识符。
从工作线程发布消息
到老板线程使用
PostThreadMessage。这
这里的缺点是老板
线程必须有一个窗口句柄
(请参阅 Classes.AllocateHWnd 中的
Delphi 帮助和 David Heffernan 的评论如下)。
使用优质的第三方
线程库。看
OmniThreadLibrary - 它是免费的,
操作系统,写得非常好。
我的选择是第三个。 Primoz 已经为您完成了所有艰苦的工作。 :)
在您发表评论后,这是与我的第一个建议类似的内容。请注意,这是未经测试的,因为为 TBoss 和 TWorker 线程 + 测试应用程序编写代码对于我此刻的时间来说有点长......这应该足以为您提供要点,我希望。
Synchronize
is specifically designed to execute code in the main thread; that's why it seems to lock everything up.You can use several ways to communicate from the worker threads to the boss thread:
Add a callback to each worker thread,
and assign it from the boss thread
when it's created. It can pass back
whatever as parameters, along with a
thread ID or some other identifier.
Post a message from the worker thread
to the boss thread using
PostThreadMessage. The
disadvantage here is that the boss
thread has to have a window handle
(see Classes.AllocateHWnd in the
Delphi help and David Heffernan's comment below).
Use a good quality third-party
threading library. See
OmniThreadLibrary - it's free,
OS, and extremely well written.
My choice would be the third. Primoz has done all the hard work for you. :)
After your comment, here's something along the lines of my first suggestion. Note that this is untested, since writing the code for a TBoss and TWorker thread + a test app is a little long for the time I have right this minute... It should be enough to give you the gist, I hope.
您可以使用线程安全队列。在DelphiXE中有TThreadedQueue。如果您没有 DXE,请尝试 OmniThreadLibray - 该库非常适合解决所有线程问题。
You could use a thread safe queue. In DelphiXE there is the TThreadedQueue. If you don't have DXE, try OmniThreadLibray - this library is very good for all threading issues.
正如我在 Delphi 2009 及更高版本中提到的新选项,这里是我的博客中基于新对象锁的线程之间生产者/消费者通信示例的链接:
Delphi 中与受保护块的线程同步
As I mentioned new options in Delphi 2009 and higher, here is a link to an example for Producer / Consumer communication between threads, based on the new objct locks, in my blog:
Thread Synchronization with Guarded Blocks in Delphi
TWorker
类的public
属性必须具有get
和set
方法,因此您可以使用Triticalsection
给出属性的值。否则,您将遇到线程安全问题。您的示例似乎没问题,但在现实世界中,数千个线程访问相同的值将导致读取错误。使用关键部分..并且您不必使用任何同步。这样您就可以避免进入窗口的消息队列并提高性能。此外,如果您在 Windows 服务应用程序中使用此代码(不允许使用 Windows 消息),则此示例将不起作用。除非可以访问 Windows 消息队列,否则同步方法不起作用。public
properties of theTWorker
class MUST haveget
andset
methods, so you can use aTcriticalsection
to give the values of the properties. Otherwise, you´d be having thread-safe issues. Your example seems ok, but in the real world, with thousands of threads accessing to the same value would result in an read error. Use critical sections.. and you wouldn´t have to use any Synchronize. This way you avoid going to the message queues of windows and improve performance. Besides, if you use this code in a windows service app, (where windows messages aren´t allowed), this example wouldn´t work. The synchronize method doesn´t work unless there´s access to the windows message queue.已解决!!(答案取自问题)
针对此问题所做的修复有两个部分。
首先删除 TWorker SendBossResult 方法中的同步调用。
其次,向 TBoss 类添加一个 fProcessWorkerResult CritialSection。在 TBoss 的创建/销毁中创建并释放它。在 ProcessWorkerResults 方法中,调用 fProcessWorkerResult.Enter 和 fProcessWorkerResult.leave 围绕需要避免多个工作结果流入的代码。
以上是 Kens 代码和后续评论之后的结论。非常感谢善良的先生,向您致敬!
Solved!! (answer taken from the question)
The fixes made for this problem where two fold.
First remove the syncronization call in the TWorker SendBossResult method.
Second add a fProcessWorkerResult CritialSection to TBoss class. Create and Free this in create/destroy of the TBoss. In the ProcessWorkerResults method call fProcessWorkerResult.Enter and fProcessWorkerResult.leave around the code which needs to be safe from multiple worker results streaming in.
The above was the conclusion after Kens code and follow up comment. Many thanks kind sir, hats off to you!.