Note that there are some explanatory texts on larger screens.

plurals
  1. POWhat is TMonitor in Delphi System unit good for?
    text
    copied!<p>After reading the articles <a href="http://blogs.embarcadero.com/abauer/2008/01/17/38849" rel="noreferrer">"Simmering Unicode, bring DPL to a boil"</a> and <a href="http://blogs.embarcadero.com/abauer/2008/01/18/38850" rel="noreferrer">"Simmering Unicode, bring DPL to a boil (Part 2)"</a> of "The Oracle at Delphi" (Allen Bauer), Oracle is all I understand :)</p> <p>The article mentions Delphi Parallel Library (DPL), lock free data structures, <a href="http://en.wikipedia.org/wiki/Mutual_exclusion" rel="noreferrer">mutual exclusion locks</a> and <a href="http://en.wikipedia.org/wiki/Condition_variable" rel="noreferrer">condition variables</a> (this Wikipedia article forwards to '<a href="http://en.wikipedia.org/wiki/Monitor_%28synchronization%29" rel="noreferrer">Monitor (synchronization)</a>', and then introduces the new <a href="http://docwiki.embarcadero.com/VCL/en/System.TMonitor" rel="noreferrer">TMonitor record type</a> for thread synchronization and describes some of its methods.</p> <p>Are there introduction articles with examples which show when and how this Delphi record type can be used? There is some <a href="http://docwiki.embarcadero.com/VCL/en/System.TMonitor" rel="noreferrer">documentation</a> online.</p> <ul> <li><p>What is the main difference between TCriticalSection and TMonitor?</p></li> <li><p>What can I do with the <code>Pulse</code> and <code>PulseAll</code>methods?</p></li> <li><p>Does it have a counterpart for example in C# or the Java language?</p></li> <li><p>Is there any code in the RTL or the VCL which uses this type (so it could serve as an example)?</p></li> </ul> <hr> <p>Update: the article <a href="http://blogs.teamb.com/craigstuntz/2009/03/25/38138/" rel="noreferrer">Why Has the Size of TObject Doubled In Delphi 2009?</a> explains that every object in Delphi now can be locked using a TMonitor record, at the price of four extra bytes per instance.</p> <p>It looks like TMonitor is implemented similar to <a href="http://download-llnw.oracle.com/javase/tutorial/essential/concurrency/locksync.html" rel="noreferrer">Intrinsic Locks in the Java language</a>:</p> <blockquote> <p>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.</p> </blockquote> <p><a href="http://docwiki.embarcadero.com/VCL/en/System.TMonitor.Wait" rel="noreferrer">Wait</a>, <a href="http://docwiki.embarcadero.com/VCL/en/System.TMonitor.Pulse" rel="noreferrer">Pulse</a> and <a href="http://docwiki.embarcadero.com/VCL/en/System.TMonitor.PulseAll" rel="noreferrer">PulseAll</a> in Delphi seem to be counterparts of <a href="http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28%29" rel="noreferrer">wait()</a>, <a href="http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#notify%28%29" rel="noreferrer">notify()</a> and <a href="http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#notifyAll%28%29" rel="noreferrer">notifyAll()</a> in the Java programming language. Correct me if I am wrong :)</p> <hr> <p>Update 2: <strong>Example code for a Producer/Consumer application</strong> using <code>TMonitor.Wait</code> and <code>TMonitor.PulseAll</code>, based on an article about guarded methods in the <a href="http://download.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html" rel="noreferrer">Java(tm) tutorials</a> (comments are welcome):</p> <blockquote> <p>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.</p> </blockquote> <p>In this example, the data is a series of text messages, which are shared through an object of type Drop:</p> <pre><code>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. </code></pre> <p>Now this works as expected, however there is a detail which I could improve: instead of locking the whole Drop instance with <code>TMonitor.Enter(Self);</code>, I could choose a fine-grained locking approach, with a (private) "FLock" field, using it only in the Put and Take methods by <code>TMonitor.Enter(FLock);</code>.</p> <p>If I compare the code with the Java version, I also notice that there is no <code>InterruptedException</code> in Delphi which can be used to cancel a call of <code>Sleep</code>.</p> <p><strong>Update 3</strong>: in May 2011, a <a href="http://www.thedelphigeek.com/2011/05/tmonitor-bug.html" rel="noreferrer">blog entry</a> about the OmniThreadLibrary presented a possible bug in the TMonitor implementation. It seems to be related to an entry in <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=78415" rel="noreferrer">Quality Central</a>. The comments mention a patch has been provided by a Delphi user, but it is not visible.</p> <p><strong>Update 4</strong>: A <a href="http://delphitools.info/2013/06/06/tmonitor-vs-trtlcriticalsection/" rel="noreferrer">blog post</a> in 2013 showed that while TMonitor is 'fair', its performance is worse than that of a critical section.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload