- •Гоувпо «Воронежский государственный технический университет»
- •Методические указания
- •Требования к выполнению и оформлению лабораторных работ
- •Практическое применение потоков
- •Основы работы объекта tThread
- •Завершение работы потока
- •Преимущества однопоточного интерфейса пользователя
- •Метод Synchronize
- •Хранение локальных данных потоков
- •Использование объекта tThread для хранения данных
- •Threadvar: хранение локальных данных потоков с помощью интерфейса api
- •Синхронизация потоков
- •Критические разделы
- •Мьютексы
- •Семафоры
- •Библиографический список
- •Содержание
- •394026 Воронеж, Московский просп., 14
Критические разделы
Критические разделы предоставляют один из самых простых способов синхронизации потоков. Критический раздел - это некоторый раздел кода, который может выполняться одновременно только одним потоком. Если код, используемый для инициализации массива, поместить в критический раздел, то другие потоки не смогут войти в этот раздел кода до тех пор, пока первый поток не завершит его выполнение.
До использования критического раздела необходимо инициализировать его с помощью процедуры 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. Результат синхронизированной инициализации массива