- •Гоувпо «Воронежский государственный технический университет»
- •Методические указания
- •Требования к выполнению и оформлению лабораторных работ
- •Практическое применение потоков
- •Основы работы объекта tThread
- •Завершение работы потока
- •Преимущества однопоточного интерфейса пользователя
- •Метод Synchronize
- •Хранение локальных данных потоков
- •Использование объекта tThread для хранения данных
- •Threadvar: хранение локальных данных потоков с помощью интерфейса api
- •Синхронизация потоков
- •Критические разделы
- •Мьютексы
- •Семафоры
- •Библиографический список
- •Содержание
- •394026 Воронеж, Московский просп., 14
Мьютексы
По своему действию мьютексы (MUTual EXclusions - взаимоисключения) очень похожи на критические разделы, но имеют при этом два существенных отличия. Во-первых, мьютексы можно использовать для синхронизации потоков, переступай через границы процессов. Во-вторых, мьютексу можно присвоить имя и путем ссылки на этo имя создать дополнительные дескрипторы существующих обьектов мьютексов.
Примечание.
Самое большое различие между критическими разделами и такими объектами событий, как мьютексы, заключается в производительности (см. в ПМК).
Функция, используемая для создания мьютекса, называется CreateMutex(), а ее объявление вьглядит следующим образом:
function CreateMutex (lpMutesAttributes: PSecurityAttributes;
bInitialOwner: BOOL; lpName: PChar) : THandle; stdcall;
Парамeтp lpMutexAttributes - это указатель на запись TSecurityAttributes. Обычно в качестве этого параметра передается значение nil, и в этом случае используются атрибуты защиты, действующие по умолчанию.
Параметр bInitialOwner определяет, следует ли считать поток, создающий мьютекс, его владельцем. Если этот параметр равен False, мьютекс не имеет владельца.
Параметр lpWame представляет ими мьютекса. Если вы не собираетесь присваивать мьютексу имя, установите этот параметр равным nil. Если же значение этого параметра отлично от nil, функция выполнит в системе поиск мьютекса с таким же именем. При успешном завершении поисковых работ функция возвратит дескриптор найденного мьютекса, в противном случае возвращается дескриптор новою мьютекса.
По завершении использования мьютекса необходимо закрыть его с помощью функции API CloseHandle ().
Задание 3. Составить программу для задачи Задания 1. Выполнение инициализаций реализовать в двух отдельных потоках с использованием мьютексов для синхронизации потоков.
Один из способов решения данной задачи следующий.
В этом случае для управления входом потоков в блок синхронизации используется функция WaitForSingleObject (), объявленная следующим образом:
function WaitForSingleObject (hHandle: THandle; dwMilliseconds; DWORD): DWORD; stdcall;
Эта функция предназначена для перевода текущего потока в состояние ожидания, пока обьект API, заданный параметром hHandle, не станет доступным. При этом состояние ожидания может продлиться вплоть до истечения интервала времени, заданного в миллисекундах параметром dwMilliseconds. Термин "доступность" для разных объектов понимается по-разному. Мьютекс становится доступным, когда он больше не принадлежит потоку, в то время как, например, процесс становится доступным после его завершения. Помимо реального периода времени, параметр dwMilliseconds может также иметь нулевое значение, которое указывает на необходимость проверки состояния объекта и немедленный возврат, или значение INFINITE, указывающее ожидать до тех пор, пока объект не станет доступным. Значения, которые может возвращать эта функция, перечислены ниже.
Возвращаемые значения функции WaitForSingleObject
Значение |
Описание |
WAIT_ABAHDONED
|
Заданный объект является объектом мьютекса, но поток - владелец этого мьютекса был завершен до его освобождения. Такой мьютекс считается отвергнутым (abandoned mutex), и в этом случае право собственности на объект мьютекса передается вызывающему потоку, а сам мьютекс определяется как недоступный |
WAIT_OBJECT_0 |
Состояние заданного объекта определяется как доступное |
WAIT_TIMEOUT |
Заданный интервал времени истек, но состояние объекта определяется как недоступное |
Итак, если мьютекс не принадлежит потоку, он находится в доступном состоянии. Первому потоку, вызвавшему функцию WaitForSingleObject () с запросом на этот мьютекс, передается право собственности на него, а состояние самого объекта мьютекса устанавливается недоступным. Когда поток вызывает функцию ReleaseMutex (), передавая в качестве параметра дескриптор мьютекса, право собственности на мьютекс у этого потока отбирается, а мьютекс вновь становится доступным.
Пример 3 модуля для реализации способа синхронизации потоков с использованием мьютексов.
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrla;
type
TMainForm = class (TForm)
Button1 : TButton;
ListBoxl : TListBox;
procedure Button1Click (Sender: TObject) ;
private
procedure ThreadsDone (Sender: TObject) ;
end;
TFooThread = class (TThread)
protected
procedure Execute; override;
end;
var
MainForm: TMainForm;
implementation
const
MaxSize = 128;
var
NextNumber : Integer = 0;
DoneFlags: Integer = 0;
GlobalArray : array [1 .. MaxSize] of Integer;
hMutex: THandle = 0;
function GetNextNumber: Integer;
begin
Result := NextNumber; // Функция возвращает глобальную переменную
Inc (NextNumber) ; Инкремент глобальной переменной
end;
procedure TFooThread. Execute;
var
i: Integer;
begin
FreeOnTerminate := True;
OnTerminate := MainForm.ThreadsDone;
if WaitForSingleObject (hMutex, INFINITE) = WAIT_OBJECT_0 then
begin
for i := 1 to MaxSize do
begin
GlobalArray[i] := GetNextNumber;
// Устанавливаем элемент массива
Sleep (5); // Позволяем потокам "перемешаться"
end;
end;
ReleaseMutex(hMutex);
end;
end;
procedure TMainForm.ThreadsDone(Sender: TObject);
var
i: integer;
begin
Inc(DoneFlaga);
if DoneFlags = 2 then // Oбa потока завершены
begin
for i := 1 to MaxSize do
{ Заполняем список элементами массива }
ListBox1.Items.Add(IntToStr(GlobalArray[i]));
CloseHandle (hMutex);
end;
end;
procedure TMainForm.Button1Click(Sender: TObject);
begin
hMutex := CreateMutex(nil, False, nil);
TFooThread.Create(False); // Создаем потоки
TFooThread.Create(False) ;
end;
end.