¿Para qué sirve TMonitor en Delphi System unit?


Después de leer los artículos "Simmering Unicode, bring DPL to a boil" y "Simmering Unicode, bring DPL to a boil (Part 2)" de "The Oracle at Delphi" (Allen Bauer), Oracle es todo lo que entiendo :){[14]]}

El artículo menciona la Biblioteca Paralela Delphi (DPL), bloquear estructuras de datos libres, bloqueos de exclusión mutua y variables de condición (este artículo de Wikipedia reenvía a ' Monitor (synchronization) ', y luego introduce TMonitor escribe el registro para la sincronización de subprocesos y describe algunos de sus métodos.

¿Hay artículos de introducción con ejemplos que muestren cuándo y cómo se puede usar este tipo de registro Delphi? Hay algo de documentación en línea.

  • ¿Cuál es la principal diferencia entre TCriticalSection y TMonitor?

  • ¿Qué puedo hacer con los métodos Pulse y PulseAll?

  • Tiene una contraparte por ejemplo, en C# o el Lenguaje Java?

  • ¿Hay algún código en la RTL o la VCL que use este tipo (por lo que podría servir como ejemplo)?


Actualización: el artículo ¿Por qué se ha Duplicado el Tamaño de TObject En Delphi 2009? explica que cada objeto en Delphi ahora se puede bloquear usando un registro TMonitor, al precio de cuatro bytes adicionales por instancia.

Parece que TMonitor está implementado de manera similar a Bloqueos intrínsecos en Java idioma :

Cada objeto tiene un bloqueo intrínseco asociado con él. Por convencióna, hilo que necesita exclusiva y acceso consistente a un objeto campos tiene que adquirir el objeto bloqueo intrínseco antes de acceder a ellos, y luego liberar el bloqueo intrínseco cuando termine con ellos.

Esperar, Pulso y PulseAll en Delphi parecen ser contrapartes de esperar(), notify() y notifyAll() en el lenguaje de programación Java. Corrígeme si me equivoco:)


Actualización 2: Código de ejemplo para una aplicación de Productor/Consumidor usando TMonitor.Wait y TMonitor.PulseAll, basado en un artículo sobre métodos guardados en los tutoriales de Java (tm) (los comentarios son bienvenidos):

Este tipo de aplicación comparte datos entre dos hilos: el productor, que crea los datos, y el consumidor, que hace algo con él. Los dos hilos comunicarse usando un objeto compartido. La coordinación es esencial: el hilo del consumidor debe no intentar recuperar los datos antes de que el hilo productor tenga lo entregó, y el hilo productor no debe intentar entregar nuevos datos si el consumidor no ha recuperado el datos antiguos.

En este ejemplo, los datos son una serie de mensajes de texto, que se comparten a través de un objeto de tipo 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.

Ahora esto funciona como se esperaba, sin embargo hay un detalle que podría mejorar: en lugar de bloquear toda la instancia Drop con TMonitor.Enter(Self);, podría elegir un enfoque de bloqueo de grano fino, con un campo "FLock" (privado), usándolo solo en los métodos Put y Take de TMonitor.Enter(FLock);.

Si comparo el código con la versión Java, también noto que no hay InterruptedException en Delphi que se pueda usar para cancelar una llamada de Sleep.

Actualización 3: en mayo de 2011, una entrada de blog sobre OmniThreadLibrary presentó un posible error en el TMonitor aplicación. Parece estar relacionado con una entrada en Quality Central . Los comentarios mencionan que un parche ha sido proporcionado por un usuario de Delphi, pero no es visible.

Actualización 4 : Una entrada de blog en 2013 mostró que mientras TMonitor es 'justo', su rendimiento es peor que el de una sección crítica.

Author: mjn, 2010-07-31

1 answers

TMonitor combina la noción de una sección crítica (o un simple mutex) junto con una variable de condición. Puedes leer sobre lo que es un "monitor" aquí: http://en.wikipedia.org/wiki/Monitor_%28synchronization%29 .

En cualquier lugar en el que usaría una sección crítica, puede usar un monitor. En lugar de declarar una sección TCriticalSection, simplemente puede crear una instancia TObject y luego usarla.

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

Donde FLock es cualquier instancia de objeto. Normalmente, sólo creo un TObject:

FLock := TObject.Create;
 6
Author: Allen Bauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-31 17:34:07