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

Мьютексы

По своему действию мьютексы (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.