閱讀文章"Simmering Unicode, bring DPL to a boil"和「甲骨文在德爾福」(艾倫·鮑爾)"Simmering Unicode, bring DPL to a boil (Part 2)"之後,甲骨文是我理解:)什麼是德爾福系統單元中的TMonitor?
文中提到德爾福並行庫(DPL),無鎖數據結構,mutual exclusion locks和condition variables (此維基百科的文章轉發到「Monitor (synchronization)」,然後引入了新的TMonitor record type線程同步,並說明了它的一些方法。
與實例表明,當這德爾福記錄類型如何使用有介紹文章?有有些documentation在線。
TCriticalSection和TMonitor的主要區別是什麼?
如何使用
Pulse
和PulseAll
方法?在C#或Java語言中是否有對應的例子?
在使用此類型的RTL或VCL中是否有任何代碼(因此它可以作爲示例)?
更新:文章Why Has the Size of TObject Doubled In Delphi 2009?解釋說,在Delphi中的每個對象現在可以使用TMonitor記錄被鎖定,在每個實例四個額外的字節的價格。
看起來TMonitor的實現方式類似於Intrinsic Locks in the Java language:
每個對象都有與之相關聯的內部鎖 。按照慣例,需要獨佔和 一致訪問對象的 字段的線程在訪問它們之前必須獲取對象的內部鎖, ,然後在它們完成時釋放內部鎖 。
Wait,Pulse和德爾福PulseAll似乎是在Java編程語言的wait(),notify()和notifyAll()同行。糾正我,如果我錯了:)
更新2:使用TMonitor.Wait
和TMonitor.PulseAll
,根據有關在Java(tm) tutorials把守方法的文章生產者/消費者應用示例代碼(評論歡迎):
這種應用程序共享數據 的兩個線程之間:創建該數據的製造者, 和 消費者,做一些事吧。 這兩個線程使用一個 共享對象進行通信。協調是必不可少 :消費者線程必須 不要試圖檢索數據 生產者線程 交付之前,和生產者線程 不能嘗試提供新的數據 如果消費者沒有檢索到的 舊數據。
在這個例子中,數據是一系列文本消息,這些消息通過類型掉落的目的共享的:
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);
來鎖定整個Drop實例,而不是使用TMonitor.Enter(FLock);
中的Put和Take方法,只用一個(私有)「FLock」字段來選擇細粒度的鎖定方法。
如果我將代碼與Java版本進行比較,我還注意到Delphi中沒有InterruptedException
可用於取消撥打Sleep
的呼叫。
更新3:2011年5月,關於OmniThreadLibrary的blog entry提出了TMonitor實現中的一個可能的錯誤。這似乎與Quality Central中的條目有關。評論提到一個補丁是由Delphi用戶提供的,但它不可見。
更新4:2013年的A blog post表明雖然TMonitor是'公平的',但其性能比臨界區差。
'TMonitor'有嚴重的錯誤,這是最後的XE2 UPD 4.糾正的錯誤可以通過在'TThreadedQueue'的TMonitor使用得到體現。有關更多信息,請參見['TThreadedQueue不支持多個消費者?](http://stackoverflow.com/q/4856306/576719)。 – 2013-05-07 09:03:24