Главная » Delphi » Критические секции

0

представляют собой  один  из  самых  простых способов син хронизации потоков. Критическая секция (critical section) — это участок кода, который в каждый  момент времени может  выполняться только  одним  из  потоков. Если  код, используемый для инициализации массива,  поместить в критическую секцию, то дру гие потоки не смогут использовать этот  участок  кода до тех пор,  пока  первый поток не завершит его выполнение.

Прежде чем  использовать критическую секцию, ее  необходимо инициализиро вать с помощью функции интерфейса API Win32 InitializeCriticalSection(), которая определяется следующим образом:

procedure InitializeCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;

Параметр lpCriticalSection представляет собой  запись  типа  TRTLCritical- Section, которая передается по ссылке.  Точное определение записи TRTLCriti- calSection не имеет  большого значения, поскольку  в ее содержимое вряд  ли пона добится когда либо заглядывать. Необходимо лишь передать неинициализированную запись в параметр lpCriticalSection, и она будет тут же заполнена процедурой.

НА ЗАМЕТКУ

Microsoft преднамеренно не документирует структуру записи TRTLCriticalSection, поскольку ее содержимое варьируется при переходе от одной аппаратной платформы к другой, а также потому, что некорректное обращение к содержимому этой структуры спо-собно нарушить работу процесса. В системах, построенных на платформе Intel, структура критического раздела содержит счетчик, поле с дескриптором текущего потока и (возможно) дескриптор системного события. В системах, построенных на платформе Alpha, счетчик заменен структурой данных Alpha-CPU, называемой взаимоблокировкой (spinlock), которая эффективнее решения Intel.

После  заполнения записи в программе можно  создать  критическую секцию, по местив  блок кода между вызовами функций API EnterCriticalSection() и Leave- CriticalSection(). Эти процедуры определяются следующим образом:

procedure EnterCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;

procedure LeaveCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;

Нетрудно догадаться, что параметр lpCriticalSection, который передается этим процедурам, является не чем иным,  как записью, созданной процедурой Initialize- CriticalSection().

По  окончании работы с  записью TRTLCriticalSection необходимо освобо дить  ее,  вызвав  функцию  API DeleteCriticalSection(), которая определяется следующим образом:

procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;

Листинг 5.5 демонстрирует способ  синхронизации потоков, инициализирующих массив с помощью  критических секций.

Листинг 5.5. Использование критических секций

unit Main; interface uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TMainForm = class(TForm)

Button1: TButton;

ListBox1: TListBox;

procedure Button1Click(Sender: TObject);

private

procedure ThreadsDone(Sender: TObject);

end;

TFooThread = class(TThread)

protected

procedure Execute; override;

end;

varMainForm: TMainForm;

implementation

{$R *.DFM}

const

MaxSize = 128;

var

NextNumber: Integer = 0;

DoneFlags: Integer = 0;

GlobalArray: array[1..MaxSize] of Integer;

CS: TRTLCriticalSection;

function GetNextNumber: Integer;

begin

Result := NextNumber;      // возвращает глобальную переменную

inc(NextNumber);           // инкремент глобальной переменной

end;

procedure TFooThread.Execute;

var

i: Integer;

begin

OnTerminate := MainForm.ThreadsDone;

EnterCriticalSection(CS);             // начало критической секции

for i := 1 to MaxSize do begin

GlobalArray[i] := GetNextNumber; // запись элемента массива

Sleep(5);                             // смена потока

end;

LeaveCriticalSection(CS);             // конец критической секции

end;

procedure TMainForm.ThreadsDone(Sender: TObject);

var

i: Integer;

begin

inc(DoneFlags);

if DoneFlags = 2 then begin        // если оба потока завершены

for i := 1 to MaxSize do

{ заполнить список элементами массива }

Listbox1.Items.Add(IntToStr(GlobalArray[i]));

DeleteCriticalSection(CS);

end;

end;

procedure TMainForm.Button1Click(Sender: TObject);

begin

InitializeCriticalSection(CS);

TFooThread.Create(False);      // создать потоки

TFooThread.Create(False);

end;

end.

После  того как первый поток  вызовет процедуру  EnterCriticalSection(), всем другим  потокам вход  в этот  блок  кода  будет запрещен. Следующий  поток, который дойдет  до этой  строки кода, будет остановлен, т.е. перейдет в состояние ожидания до тех пор,  пока первый поток  не вызовет процедуру  LeaveCriticalSection(). В этот момент система  возобновит второй поток  и разрешит ему пройти через критическую секцию.  На  рис. 5.5 показан результат  работы этого  приложения, когда  потоки уже синхронизированы.

Рис. 5.5. Результат синхронизированной инициализации массива

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии