2010-07-31 15 views
28

閱讀文章"Simmering Unicode, bring DPL to a boil"和「甲骨文在德爾福」(艾倫·鮑爾)"Simmering Unicode, bring DPL to a boil (Part 2)"之後,甲骨文是我理解:)什麼是德爾福系統單元中的TMonitor?

文中提到德爾福並行庫(DPL),無鎖數據結構,mutual exclusion lockscondition variables (此維基百科的文章轉發到「Monitor (synchronization)」,然後引入了新的TMonitor record type線程同步,並說明了它的一些方法。

與實例表明,當這德爾福記錄類型如何使用有介紹文章?有有些documentation在線。

  • TCriticalSection和TMonitor的主要區別是什麼?

  • 如何使用PulsePulseAll方法?

  • 在C#或Java語言中是否有對應的例子?

  • 在使用此類型的RTL或VCL中是否有任何代碼(因此它可以作爲示例)?


更新:文章Why Has the Size of TObject Doubled In Delphi 2009?解釋說,在Delphi中的每個對象現在可以使用TMonitor記錄被鎖定,在每個實例四個額外的字節的價格。

看起來TMonitor的實現方式類似於Intrinsic Locks in the Java language

每個對象都有與之相關聯的內部鎖 。按照慣例,需要獨佔和 一致訪問對象的 字段的線程在訪問它們之前必須獲取對象的內部鎖, ,然後在它們完成時釋放內部鎖 。

WaitPulse和德爾福PulseAll似乎是在Java編程語言的wait()notify()notifyAll()同行。糾正我,如果我錯了:)


更新2:使用TMonitor.WaitTMonitor.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是'公平的',但其性能比臨界區差。

+0

'TMonitor'有嚴重的錯誤,這是最後的XE2 UPD 4.糾正的錯誤可以通過在'TThreadedQueue'的TMonitor使用得到體現。有關更多信息,請參見['TThreadedQueue不支持多個消費者?](http://stackoverflow.com/q/4856306/576719)。 – 2013-05-07 09:03:24

回答

4

TMonitor將臨界區(或簡單互斥區)的概念與條件變量結合在一起。你可以閱讀關於「監視器」在這裏的內容:http://en.wikipedia.org/wiki/Monitor_%28synchronization%29

任何地方你會使用關鍵部分,你可以使用顯示器。不用聲明一個TCriticalSection,你可以簡單地創建一個TObject實例,然後使用它。

TMonitor.Enter(FLock); 
try 
    // protected code 
finally 
    TMonitor.Exit(FLock); 
end; 

其中FLock是任何對象實例。通常情況下,我只需創建一個TObject的:

FLock := TObject.Create; 
+0

所以這個'FLOCK'是這個例子中的顯示器? (如維基百科中所描述的,「旨在由多於一個線程安全使用的對象」) – mjn 2010-07-31 17:26:54

+0

FLock是任何對象實例。它可以只是一個簡單的TObject實例。 FLock:= TObject.Create; – 2010-07-31 17:31:29

+5

還不夠。您已經展示瞭如何使用TMonitor模擬關鍵部分,但確定這不是TMonitor設計的真正問題。你能給出一個更有趣的代碼示例嗎? – kludg 2010-07-31 17:53:16