После чтения статей "Simmering Unicode, bring DPL to a boil" и "Simmering Unicode, bring DPL to a boil (Part 2)" "Oracle в Delphi" (Allen Bauer), Oracle - все, что я понимаю :)
Статья упоминает Библиотеку параллели Delphi (DPL), заблокируйте бесплатные структуры данных, блокировки взаимного исключения и условные переменные (эта статья Wikipedia вперед для 'Контроля (синхронизация)', и затем представляет новый тип записи TMonitor для синхронизации потока и описывает некоторые ее методы.
Есть ли вводные статьи с примерами, которые показывают, когда и как этот тип записи Delphi может использоваться? Онлайн существует некоторая документация.
Каково основное различие между TCriticalSection и TMonitor?
Что я могу сделать с Pulse
и PulseAll
методы?
Это имеет дубликат, например, в C# или языке Java?
Есть ли какой-либо код в RTL или VCL, который использует этот тип (таким образом, он мог служить примером)?
Обновление: статья Why Has the Size of TObject Doubled In Delphi 2009? объясняет, что каждый объект в Delphi теперь может быть заблокирован с помощью записи TMonitor по цене четырех дополнительных байтов на экземпляр.
Похоже, что TMonitor реализован подобный Внутренним Блокировкам на языке Java:
Каждый объект имеет внутреннюю блокировку, связанную с ним. Условно, поток, которому нужен эксклюзивный и последовательный доступ к полям объекта, должен получить внутреннюю блокировку объекта прежде, чем получить доступ к ним и затем выпустить внутреннюю блокировку, когда это сделано с ними.
Ожидайте, Пульсируйте, и PulseAll в Delphi, кажется, дубликаты ожидания (), уведомляют () и notifyAll () на языке программирования Java. Исправьте меня, если я неправ :)
Обновление 2: Пример кода для Производителя/Пользовательского приложения, использующего TMonitor.Wait
и TMonitor.PulseAll
, на основе статьи о защищенных методах в Java учебные руководства (по TM) (комментарии приветствуются):
Этот вид приложения обменивается данными между двумя потоками: производитель, который создает данные и потребителя, который делает что-то с ними. Два потока передают использование общего объекта. Координация важна: потребительский поток не должен пытаться получить данные, прежде чем поток производителя поставил его, и поток производителя не должен пытаться поставить новые данные, если потребитель не получил старые данные.
В этом примере данные являются рядом текстовых сообщений, которые совместно используются через объект Отбрасывания типа:
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", с помощью него только в Помещенном и Взять методы TMonitor.Enter(FLock);
.
Если я сравниваю код с версией Java, я также замечаю, что существует нет InterruptedException
в Delphi, который может использоваться для отмены вызова Sleep
.
Обновление 3: в мае 2011 запись в блоге о OmniThreadLibrary представила возможную ошибку в реализации TMonitor. Это, кажется, связано с записью, по качеству Центральной. Комментарии упоминают, что патч был обеспечен пользователем Delphi, но это не видимо.
Обновление 4: сообщение в блоге в 2013 показало, что, в то время как TMonitor 'справедлив', его производительность хуже, чем тот из критического раздела.
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;