代码之家  ›  专栏  ›  技术社区  ›  bk1e

Delphi系统单元中的TMonitor有什么用?

  •  31
  • bk1e  · 技术社区  · 5 年前

    看完文章 "Simmering Unicode, bring DPL to a boil" "Simmering Unicode, bring DPL to a boil (Part 2)" 在《德尔福的先知》(艾伦·鲍尔)一书中,我所理解的只有先知:)

    mutual exclusion locks condition variables (这篇维基百科文章转发给' Monitor (synchronization) ,然后介绍新的 TMonitor record type 并介绍了它的一些方法。

    是否有介绍文章和例子来说明何时以及如何使用这种Delphi记录类型?有一些 documentation 在线。

    • TCriticalSection和TMonitor的主要区别是什么?

    • 我能拿这个做什么 Pulse PulseAll 方法?

    • RTL或VCL中是否有使用此类型的代码(因此可以作为示例)?


    更新:文章 Why Has the Size of TObject Doubled In Delphi 2009?

    Intrinsic Locks in the Java language

    每个对象都有一个内在锁 一致访问对象的 字段必须获取对象的 在访问它们之前, 然后释放内在锁

    Wait Pulse PulseAll 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); ,我可以选择一种细粒度的锁定方法,带有一个(私有)“FLock”字段,只在Put和Take方法中使用它 TMonitor.Enter(FLock); .

    InterruptedException 在Delphi中可以用来取消 Sleep .

    更新3 :2011年5月 blog entry Quality Central . 注释中提到了一个由Delphi用户提供的修补程序,但它不可见。

    更新4 blog post 2013年的研究表明,尽管TMonitor是“公平的”,但它的性能比关键部分差。

    1 回复  |  直到 11 年前
        1
  •  8
  •   Thomas Ahle    14 年前

    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;