在阅读了文章"Simmering Unicode,让DPL沸腾"和"Simmering Unicode,让DPL沸腾(第2部分)"的"Oracle Delphi"(Allen Bauer)之后,Oracle就是我理解的全部:)
文章提到了Delphi Parallel Library(DPL),锁定免费数据结构,互斥锁和条件变量(这篇维基百科文章转发到' Monitor(同步) ',然后介绍了用于线程同步的新TMonitor记录类型并描述了它的一些方法.
是否有介绍文章,其中的示例显示了何时以及如何使用此Delphi记录类型?网上有一些文档.
TCriticalSection和TMonitor之间的主要区别是什么?
我能做些什么与Pulse
和PulseAll
方法呢?
它是否具有例如C#或Java语言的对应物?
RTL或VCL中是否有使用此类型的代码(因此它可以作为示例)?
更新:文章为什么在Delphi 2009中TObject的大小翻了一番?解释说现在可以使用TMonitor记录锁定Delphi中的每个对象,每个实例需要额外四个字节.
看起来TMonitor的实现类似于Java语言中的Intrinsic Locks:
每个对象都有一个与之关联的内在锁.按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁.
等待,Delphi中的Pulse和PulseAll似乎是Java编程语言中wait(),notify()和notifyAll()的对应物.如果我错了,请纠正我:)
更新2:生产者/消费者应用程序的示例代码,使用TMonitor.Wait
和TMonitor.PulseAll
基于Java(tm)教程中有关保护方法的文章(欢迎评论):
这种应用程序在两个线程之间共享数据:生成器,创建数据,以及使用它的消费者.两个线程使用共享对象进行通信.协调是必不可少的:消费者线程不得在生产者线程交付之前尝试检索数据,并且如果消费者未检索旧数据,则生产者线程不得尝试传递新数据.
在此示例中,数据是一系列文本消息,通过Drop类型的对象共享:
program TMonitorTest;
// based on example code at http://download.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
type
Drop = …
Run Code Online (Sandbox Code Playgroud) 我很高兴看到Delphi在Delphi 2009中引入TMonitor记录,允许您在多线程环境中锁定特定对象.令我困惑的是这种记录类型的Pulse和PulseAll方法.
例如,Delphi的Pulse中的条目帮助指出"通知等待队列中的下一个线程,一旦调用线程释放对象,它就能锁定指定的对象."
真?那是什么意思?我使用过TMonitor而没有使用Pulse而没有问题.此外,在Delphi的源代码中,TMonitor的一些用途从不使用Pulse.
Pulse和PulseAll方法是否仅包含在Delphi的TMonitor记录中,用于与.NET Monitor类的源级兼容性,还是它们真的有用?
有两个问题("TMonitor.Pulse与TMonitor.PulseAll"和"Delphi系统单元中的TMonitor是什么有利于")这个问题,但我正在寻找一个明确的答案.
Delphi Docwiki解释说,Pulse通知等待队列中的下一个线程,一旦调用线程释放对象,它就能锁定指定的对象.PulseAll发信号通知等待队列中的所有线程.
我发现这个代码在线程队列实现中使用Pulse,并且根据上面的定义,认为应该使用PulseAll - 或者以不同的方式询问:何时使用Pulse而不是PulseAll是正确的?(基本问题在于:如何确保队列中的'下一个线程'是需要通知的线程,除非在总共只有两个线程的简单情况下,或者代码可以安全假设唯一等待的线程是需要被nofified /'pulsed'的线程?)?
function TSimpleThreadedQueue.Enqueue(const Item: T; Timeout: LongWord): TWaitResult;
begin
if Closed then Exit(wrAbandoned);
if not TMonitor.Enter(FQueue, Timeout) then Exit(wrTimeout);
try
if Closed then Exit(wrAbandoned);
FQueue.Enqueue(Item);
TMonitor.Pulse(FQueue);
Result := wrSignaled;
finally
TMonitor.Exit(FQueue);
end;
end;
Run Code Online (Sandbox Code Playgroud)
对于Java语言中的相应同步方法,我发现了这个问题:Java:notify()与notifyAll()一遍又一遍
更新:上面链接的Java问题有一个有趣的答案,它显示了即使在生成器/消费者应用程序中也会发生死锁,该应用程序只有两个方法,put和get,以及使用notify()(Pulse()的Java副本) :Java:再次通知()和notifyAll()
答案包含建议
如果您不确定使用哪个,请使用
notifyAll
.
我回来了另一个关于线程和同步的问题.想象一下,服务器应用程序必须执行冗长的操作,并且客户端希望他的GUI在等待服务器的响应时保持响应.我想到了以下模式:
TMonitor.Enter (FTCPClient);
try
WorkerThread := TWorkerThread.Create (SomeLengthyServerOperation);
while (not WorkerThread.Ready) do
Application.ProcessMessages;
DoSometingWithResults (WorkerThread.Result);
WorkerThread.Free;
finally
TMonitor.Exit (FTCPClient);
end;
Run Code Online (Sandbox Code Playgroud)
WorkerThread是一个派生自TThread的简单类,它执行传递给其构造函数的函数,然后终止(使用Ready = True,结果为Result).只要单击按钮,就会执行显示的代码.
现在我的问题:如果我非常快地点击按钮两次,我会得到一些奇怪的错误,看起来很像服务器和客户端之间的通信以某种方式被误解,我想通过锁定FTCPClient对象来避免.Application.ProcessMessages执行后的事件处理程序是什么线程?是每个线程的TMonitor锁吗?这是否意味着如果我使用Application.ProcessMessages,锁不起作用?
我现在无法更好地解释它.我希望有人明白我的观点.如果没有,请随时提问.
编辑:对于按钮的禁用和启用:我对客户端代码一无所知.可能是一个按钮事件处理程序,可能是其他东西.基本上我想隐藏客户端代码的锁定.
我目前正致力于将现有的Delphi 5应用程序移植到Delphi 2010.
它是加载到Outlook中的多线程DLL(由Outlook生成的线程).当通过Delphi 2010编译时,每当我关闭一个表单时,我都会遇到TMonitor.Destroy中的"无效指针操作"......即system.pas中的那个.
由于这是一个现有的,有点复杂的应用程序,我有很多方向需要研究,而delphi帮助甚至没有记录这个特定的TMonitor类的文档(我将其追溯到一些Allen Bauer帖子以及附加信息) )...所以我想我先问一下是否有人曾经遇到过这个问题,或者对于什么可能导致这个问题有任何建议.为了记录:我没有在我的代码中明确使用TMonitor功能,我们在这里谈论Delphi 5代码的直接端口.
在问题发生时编辑 Callstack:
System.TMonitor.Destroy
System.TObject.Free
Forms.TCustomForm.CMRelease(???)
Controls.TControl.WndProc(???)
Controls.TWinControl.WndProc((45089, 0, 0, 0, 0, 0, 0, 0, 0, 0))
Forms.TCustomForm.WndProc(???)
Controls.TWinControl.MainWndProc(???)
Classes.StdWndProc(15992630,45089,0,0)
Forms.TApplication.ProcessMessage(???)
Run Code Online (Sandbox Code Playgroud)