文章提到Delphi并行库(DPL),无锁数据结构,mutual exclusion locks和condition variables(这篇维基百科文章转发到“Monitor (synchronization)”,然后介绍了新的TMonitor record type线程同步,并描述了它的一些方法。
有没有介绍文章与示例,显示何时和如何这个Delphi记录类型可以使用?有一些documentation在线。
> TCriticalSection和TMonitor之间的主要区别是什么?
>我可以用Pulse和PulseAllmethods做什么?
>它有一个对应的例如在C#或Java语言?
>在RTL或VCL中是否有使用此类型的代码(因此它可以作为示例)?
更新:文章Why Has the Size of TObject Doubled In Delphi 2009?解释说,现在可以使用TMonitor记录锁定Delphi中的每个对象,每个实例的价格为4个额外字节。
看起来TMonitor的实现类似于Intrinsic Locks in the Java language:
Every object has an intrinsic lock
associated with it. By convention,a
thread that needs exclusive and
consistent access to an object’s
fields has to acquire the object’s
intrinsic lock before accessing them,
and then release the intrinsic lock
when it’s done with them.
在Delphi中的Wait,Pulse和PulseAll似乎是Java编程语言中的wait(),notify()和notifyAll()的对应物。纠正我,如果我错了:)
更新2:使用TMonitor.Wait和TMonitor.PulseAll的生产者/消费者应用程序的示例代码,基于Java(tm) tutorials中有关保护方法的文章(欢迎提出意见):
This kind of application shares data
between two threads: the producer,
that creates the data,and the
consumer,that does something with it.
The two threads communicate using a
shared object. Coordination is
essential: the consumer thread must
not attempt to retrieve the data
before the producer thread has
delivered it,and the producer thread
must not attempt to deliver new data
if the consumer hasn’t retrieved the
old data.
在此示例中,数据是一系列文本消息,通过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 = class(TObject) private // Message sent from producer to consumer. Msg: string; // True if consumer should wait for producer to send message,false // if producer should wait for consumer to retrieve message. Empty: Boolean; public constructor Create; function Take: string; procedure Put(AMessage: string); end; Producer = class(TThread) private FDrop: Drop; public constructor Create(ADrop: Drop); procedure Execute; override; end; Consumer = class(TThread) private FDrop: Drop; public constructor Create(ADrop: Drop); procedure Execute; override; end; { Drop } constructor Drop.Create; begin Empty := True; end; function Drop.Take: string; begin TMonitor.Enter(Self); try // Wait until message is available. while Empty do begin TMonitor.Wait(Self,INFINITE); end; // Toggle status. Empty := True; // Notify producer that status has changed. TMonitor.PulseAll(Self); Result := Msg; finally TMonitor.Exit(Self); end; end; procedure Drop.Put(AMessage: string); begin TMonitor.Enter(Self); try // Wait until message has been retrieved. while not Empty do begin TMonitor.Wait(Self,INFINITE); end; // Toggle status. Empty := False; // Store message. Msg := AMessage; // Notify consumer that status has changed. TMonitor.PulseAll(Self); finally TMonitor.Exit(Self); end; end; { Producer } constructor Producer.Create(ADrop: Drop); begin FDrop := ADrop; inherited Create(False); end; procedure Producer.Execute; var Msgs: array of string; I: Integer; begin SetLength(Msgs,4); Msgs[0] := 'Mares eat oats'; Msgs[1] := 'Does eat oats'; Msgs[2] := 'Little lambs eat ivy'; Msgs[3] := 'A kid will eat ivy too'; for I := 0 to Length(Msgs) - 1 do begin FDrop.Put(Msgs[I]); Sleep(Random(5000)); end; FDrop.Put('DONE'); end; { Consumer } constructor Consumer.Create(ADrop: Drop); begin FDrop := ADrop; inherited Create(False); end; procedure Consumer.Execute; var Msg: string; begin repeat Msg := FDrop.Take; WriteLn('Received: ' + Msg); Sleep(Random(5000)); until Msg = 'DONE'; end; var ADrop: Drop; begin Randomize; ADrop := Drop.Create; Producer.Create(ADrop); Consumer.Create(ADrop); ReadLn; end.
现在这个工作作为预期,但有一个细节,我可以改进:而不是锁定整个Drop实例与TMonitor.Enter(Self);我可以选择一个细粒度锁定方法,与(私人)“FLock”字段,仅在通过TMonitor.Enter(FLock);在Put和Take方法中使用它。
如果我将代码与Java版本进行比较,我还注意到在Delphi中没有InterruptedException可以用来取消Sleep的调用。
更新3:2011年5月,一个关于OmniThreadLibrary的blog entry提出了TMonitor实现中可能的错误。它似乎与一个条目相关在Quality Central.评论提到的补丁是由Delphi用户提供的,但它是不可见的。
解决方法
任何地方,你会使用一个关键部分,你可以使用监视器。而不是声明一个TCriticalSection,你可以简单地创建一个TObject实例,然后使用它。
TMonitor.Enter(FLock); try // protected code finally TMonitor.Exit(FLock); end;
其中FLock是任何对象实例。通常,我只是创建一个TObject:
FLock := TObject.Create;