在阅读了“The Oracle at Delphi”(Allen Bauer)的文章“Simmering Unicode,让 DPL 沸腾”和“Simmering Unicode,让 DPL 沸腾(第 2 部分)”之后,我只了解 Oracle :)
文章提到了Delphi并行库(DPL)、无锁数据结构、互斥锁和条件变量(这篇维基百科文章转发到' Monitor(synchronization) ',然后介绍了用于线程同步的新TMonitor记录类型并描述了它的一些方法。
是否有带有示例的介绍文章,说明何时以及如何使用这种 Delphi 记录类型?网上有一些文档。
TCriticalSection 和 TMonitor 的主要区别是什么?
我可以用
Pulse
和PulseAll
方法做什么?它是否有对应的 C# 或 Java 语言?
RTL 或 VCL 中是否有任何代码使用这种类型(因此可以作为示例)?
更新:文章为什么 TObject 的大小在 Delphi 2009 中翻了一番?解释说 Delphi 中的每个对象现在都可以使用 TMonitor 记录来锁定,代价是每个实例增加四个字节。
看起来 TMonitor 的实现类似于Java 语言中的 Intrinsic Locks:
每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行排他和一致访问的线程必须在访问对象之前获取对象的内在锁,然后在完成访问时释放内在锁。
Delphi 中的Wait、Pulse和PulseAll似乎是Java 编程语言中wait()、notify()和notifyAll()的对应物。如果我错了,请纠正我:)
更新 2:使用和的生产者/消费者应用程序的示例代码,基于Java(tm) 教程中关于受保护方法的文章(欢迎评论):TMonitor.Wait
TMonitor.PulseAll
这种应用程序在两个线程之间共享数据:创建数据的生产者和处理数据的消费者。两个线程使用共享对象进行通信。协调是必不可少的:消费者线程不得在生产者线程交付数据之前尝试检索数据,如果消费者尚未检索旧数据,则生产者线程不得尝试交付新数据。
在此示例中,数据是一系列文本消息,通过 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.
现在这可以按预期工作,但是有一个细节我可以改进:TMonitor.Enter(Self);
我可以选择一种细粒度的锁定方法,使用(私有)“FLock”字段,而不是用 锁定整个 Drop 实例,仅在Put 和 Take 方法由TMonitor.Enter(FLock);
.
如果我将代码与 Java 版本进行比较,我还注意到InterruptedException
Delphi 中没有可用于取消对Sleep
.
更新 3:2011 年 5 月,一篇关于 OmniThreadLibrary 的博客文章提出了 TMonitor 实现中可能存在的错误。它似乎与Quality Central 中的条目有关。评论提到 Delphi 用户提供了一个补丁,但它是不可见的。
更新 4: 2013 年的一篇博客文章表明,虽然 TMonitor 是“公平的”,但它的性能比关键部分的性能差。