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 "El oráculo de Delfos" (Allen Bauer), Oracle es todo lo que entiendo :)¿Para qué sirve TMonitor en la unidad Delphi System?
El artículo menciona Delphi Parallel Library (DPL), bloqueo de las estructuras de datos libres, mutual exclusion locks y condition variables (esto artículo de Wikipedia hacia delante a 'Monitor (synchronization)', y luego introduce el nuevo TMonitor record type para la sincronización de hilos y describe algunos de sus métodos.
¿Hay artículos de introducción con ejemplos que muestran cuándo y cómo se puede utilizar este tipo de registro Delphi? No es un documentation en línea.
¿Cuál es la principal diferencia entre TCriticalSection y TMonitor?
¿Qué puedo hacer con los métodos
Pulse
yPulseAll
?¿Tiene una contrapartida, por ejemplo, en C# o en el lenguaje Java?
¿Hay algún código en el RTL o el VCL que utiliza este tipo (por lo que podría servir como un ejemplo)?
Actualización: el artículo Why Has the Size of TObject Doubled In Delphi 2009? explica que todos los objetos de Delphi ahora se puede bloquear mediante un registro TMonitor, al precio de cuatro bytes adicionales por cada instancia.
Parece que TMonitor está implementado similar a Intrinsic Locks in the Java language:
Cada objeto tiene un bloqueo intrínseco asociada a ella. Por convención, un hilo que necesita acceso exclusivo y coherente a campos de un objeto tiene que adquirir bloqueo intrínseca del objeto antes de acceder a ellos, y luego liberar el bloqueo intrínseco cuando se hace con ellos.
Wait, Pulse y PulseAll en Delphi parecen ser homólogos de wait(), notify() y notifyAll() en el lenguaje de programación Java. corrígeme si estoy equivocado :)
Actualización 2: Código de ejemplo para una aplicación productor/consumidor usando TMonitor.Wait
y TMonitor.PulseAll
, basado en un artículo acerca de los métodos guardados en los Java(tm) tutorials (comentarios son bienvenidos):
Este tipo de acciones de aplicaciones de datos entre dos hilos: el productor, que crea los datos, y la de consumo, que hace algo con él. Los dos hilos se comunican utilizando un objeto compartido .La coordinación es esencial : el hilo consumidor debe No intente recuperar los datos antes de que el hilo productor ha entregado, y el hilo productor no debe intentar ofrecer nuevos datos si el consumidor no ha recuperado los datos antiguos .
En este ejemplo, los datos son una serie de mensajes de texto, los cuales son compartidos 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 bien, esto funciona como se espera, sin embargo, hay un detalle que pude mejorar: en lugar de bloquear toda la instancia de Drop con TMonitor.Enter(Self);
, podría elegir un enfoque de bloqueo de grano fino, con un campo (privado) "FLock", utilizándolo solo en los métodos Put y Take por TMonitor.Enter(FLock);
.
Si comparo el código con la versión de Java, también noto que no hay InterruptedException
en Delphi que se puede utilizar para cancelar una llamada de Sleep
.
Actualización 3: en mayo de 2011, un blog entry sobre OmniThreadLibrary presentó un posible error en la implementación de TMonitor. Parece estar relacionado con una entrada en Quality Central. Los comentarios mencionan que un parche ha sido provisto por un usuario de Delphi, pero no es visible.
Update 4: Un blog post en 2013 mostraron que mientras TMonitor es 'justo', su rendimiento es peor que la de una sección crítica.
'TMonitor' tenía errores graves, que finalmente fueron corregidos en XE2 UPD 4. Los errores pueden ser manifestadas por el uso TMonitor en' TThreadedQueue'. Consulte ['TThreadedQueue no es apto para múltiples consumidores?'] (Http://stackoverflow.com/q/4856306/576719) para obtener más información. –