Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 300072.doc
Скачиваний:
2
Добавлен:
30.04.2022
Размер:
297.98 Кб
Скачать

Критические разделы

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

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

procedure initializeCriticalSection (var lpCriticalSection: TRTLCriticalSection); stdcall;

Параметр lpCriticalSection представляет собой запись типа TRTLCriticalSection, которая передается по ссылке. Точное определение записи TRTLCriticalSection не имеет большого значения, поскольку вам вряд ли понадобится когда-либо заглядывать в ее содержимое. От вас требуется лишь передать неинициализированную запись в параметр lpCriticalSection, и эта запись будет тут же заполнена процедурой.

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

procedure EnterCriticalSection (var lpCriticalSection: TRTLCriticalSection); stdcall;

procedure LeaveCriticalSection (var lpCriticalSection: TRTLCriticalSection); stdcall;

Параметр lpCriticalSection, который передается этим процедурам, является записью, заполняемой процедурой InitializeCriticalSection ().

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

procedure DeleteCriticalSection(var lpCriticalSection: TRTLCriticalSection); stdcall;

Задание 2. Составить программу для задачи Задания 1. Выполнение инициализаций реализовать в двух отдельных потоках с синхронизацией инициализирующих массив потоков с помощью критических разделов.

Один из способов решения данной задачи следующий.

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

Пример 2 модуля для реализации способа синхронизации инициализирующих массив потоков с помощью критических разделов.

unit Main;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

Type

TMainForm = class(TForm)

Button1: TButton;

ListBox1: TListBox;

procedure ButtonlClick (Sender: TObject);

private

procedure ThreadsDone(Sender: TObject);

end;

TFooThread = class(Tthread)

protected

procedure Execute; override;

end;

var

MainForm: 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 // Oбa потока завершены

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.

На рис. 6 показан результат работы приложения, когда потоки уже синхронизированы.

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