> Kerk © (07.10.08 16:11) [9]
Лучше, например, потому, что за синхронизацией следит компилятор, он не допустит одновременного обращения к переменной из разных нитей. Потому, что не зависит от платформы (для Delphi это не актуально, но для Ады, например, очень даже). Ещё потому, что синхронизация нитей осуществляется не критическими секциями и событиями, а более высокоуровневыми средствами, например, мониторами и рандеву, как в Модуле и Аде.
Монитор - это разновидность класса, область видимости полей которого ограничена методами самого монитора, извне к ним обратиться напрямую невозможно. А для методов монитора автоматически генерируется код синхронизации доступа, причём программист может управлять этим с помощью очередей. Вот, например, код монитора для хранения данных в кольцевом буфере на языке Concurrent Pascal (язык разработал Brinch Hansen в 1975 г., пример взят из книги Роберта Себесты "Основные концепции языков программирования", 5-е издание)
type DataBuf = monitor
const BufSize = 100;
var Buf: array [1..BufSize] of Integer;
NextIn, NextOut: 1..BufSize;
Filled: 0..BufSize;
Sender_q, Receiver_q: queue;
procedure entry Deposit(Item: Integer);
begin
if Filled = FufSize then
Delay(Sender_q);
Buf[NextIn] := Item;
NextIn := (NextIn mod BufSize) + 1;
Filled := Filled + 1;
Continue(Receivr_q)
end;
procedure entry Fetch(var Item: Integer);
begin
if Filled = 0 then
Delay(Receiver_q);
Item := Buf[NextOut];
NextOut := (NextOut mod BufSize) + 1;
Filled := Filled - 1;
Continue(Sender_q)
end;
begin
Filled := 0;
NextIn := 1;
NextOut := 1;
end;
Последние begin..end - это код инициализации (конструктор) монитора.
Здесь используется тип queue - стандартный тип Concurrent Pascal. Он организует очередь нитей. С очередью связаны две операции - Delay и Continue. Delay приостанавливает данную нить, помещая её в очередь. Continue извлекает нить из заданной очереди и продолжает её выполнение. Рассмотрим, например, как работает процедура монитора Fetch. Она проверяет, есть ли что-то в буфере, и если нет, приостанавливает текущую нить и помещает её в очередб Receiver_q. А процедура Deposit, помещающая данные в буфер, заканчивается вызовом Continue для этой очереди, т.е. если там стоят нити, ожидающие данных, первая из них будет продолжена после Deposit. Аналогично очередь Sender_q позволяет приостанавливать нить, кладующую данные, если буфер заполнен, и продолжать после того, как в нём освободится место. Из всех нитей методы Deposit и Fetch можно вызывать, не заботясь больше ни о какой синхронизации.