33

「The Oracle at Delphi」(Allen Bauer)の記事「Simmering Unicode、DPL を沸騰させる」および「Simmering Unicode、DPL を沸騰させる (パート 2)」を読んだ後、Oracle は私が理解するすべてです:)

この記事では、Delphi Parallel Library (DPL)、ロック フリー データ構造、相互排他ロック、および条件変数について言及しています(このウィキペディアの記事では、「モニター (同期) 」に進み、スレッド同期用の新しいTMonitor レコード タイプを紹介し、そのいくつかについて説明します。メソッド。

この Delphi レコード タイプをいつ、どのように使用できるかを示す例を含む紹介記事はありますか? オンラインでいくつかのドキュメントがあります。

  • TCriticalSection と TMonitor の主な違いは何ですか?

  • Pulseメソッドとメソッドを使用して何ができPulseAllますか?

  • たとえば、C# や Java 言語に対応するものはありますか?

  • RTL または VCL にこの型を使用するコードはありますか (例として役立つ可能性があります)。


更新: Delphi 2009 で TObject のサイズが 2 倍になった理由の記事 Delphi のすべてのオブジェクトは、TMonitor レコードを使用してロックできるようになったと説明していますが、インスタンスごとに 4 バイトの追加料金がかかります。

TMonitor は、Java 言語の Intrinsic Locksと同様に実装されているようです。

すべてのオブジェクトには固有のロックが関連付けられています。慣例により、オブジェクトのフィールドへの排他的かつ一貫したアクセスを必要とするスレッドは、オブジェクトにアクセスする前にオブジェクトの固有ロックを取得し、それらの操作が完了したら固有ロックを解放する必要があります。

Delphi のWaitPulse、およびPulseAllは、Java プログラミング言語の wait() 、 notify() 、および notifyAll() に相当するようです間違っていたら訂正してください:)


更新 2: Java(tm) チュートリアルの保護されたメソッドに関する記事に基づいて、 と を使用するProducer/Consumer アプリケーションのサンプル コード(コメントを歓迎します):TMonitor.WaitTMonitor.PulseAll

この種のアプリケーションは、データを作成するプロデューサとデータを処理するコンシューマの 2 つのスレッド間でデータを共有します。2 つのスレッドは、共有オブジェクトを使用して通信します。調整は不可欠です。コンシューマー スレッドは、プロデューサー スレッドがデータを配信する前にデータの取得を試みてはならず、コンシューマーが古いデータを取得していない場合、プロデューサー スレッドは新しいデータの配信を試みてはなりません。

この例では、データは一連のテキスト メッセージであり、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);ます。Drop インスタンス全体を でロックする代わりに、(プライベート) "FLock" フィールドを使用して、 Put および Take メソッド by TMonitor.Enter(FLock);.

InterruptedExceptionコードを Java バージョンと比較すると、Delphi には の呼び出しをキャンセルするために使用できるものがないことにも気付きSleepます。

更新 3 : 2011 年 5 月に、OmniThreadLibrary に関するブログ エントリで、TMonitor 実装のバグの可能性が示されました。Quality Centralのエントリに関連しているようです。コメントには、パッチが Delphi ユーザーによって提供されたことが記載されていますが、表示されません。

更新 4 : 2013 年のブログ投稿では、TMonitor は「公正」ですが、そのパフォーマンスはクリティカル セクションのパフォーマンスよりも悪いことが示されました。

4

1 に答える 1

9

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;
于 2010-07-31T16:42:26.790 に答える