Главная » Delphi » Мьютексы

0

По принципу своего  действия мьютексы (MUTual EXclusions  — взаимоисключения) очень  похожи на  критические секции, за  исключением двух моментов. Во первых, мьютексы можно  использовать для синхронизации потоков, переступая через грани цы процессов. Во вторых, мьютексу можно  присвоить имя,  ссылаясь на которое мож но создавать дополнительные дескрипторы существующих объектов мьютексов.

CОВЕТ

Помимо семантических особенностей, самое большое различие между критическими секциями и такими объектами событий, как мьютексы, заключается в производитель- ности. Критические секции очень эффективны: если нет конфликтов потоков, то на вход или выход из критической секции уходит всего 10–15 системных тактов процессо- ра. Но если для данной критической секции возникает конфликт потоков, то система создает                  объект                  события                  (возможно,                  мьютекс). В “стоимость” использования таких объектов событий, как мьютексы, входит обяза- тельное обращение к подпрограммам ядра, что требует переключения контекста про- цесса и смены уровня цикла, а на это уходит от 400 до 600 системных тактов процес- сора. Причем без этих дополнительных затрат нельзя обойтись даже в том случае, ес- ли в приложении в данный момент вообще нет вторичных потоков или если нет других потоков, которые оспаривают право на защищаемые ресурсы.

Функция, используемая для создания мьютекса, называется CreateMutex(), а ее объявление выглядит следующим образом:function CreateMutex(lpMutexAttributes: PSecurityAttributes;

bInitialOwner: BOOL; lpName: PChar): THandle; stdcall;

Параметр lpMutexAttributes —   это  указатель   на  запись   типа   TSecurityAt- tributes. Обычно в качестве такого  параметра передается значение nil, что приво дит к использованию атрибутов защиты, установленных по умолчанию.

Параметр bInitialOwner определяет, следует ли считать поток, создающий мью текс,  его  владельцем. Если  данный параметр равен  False, то мьютекс  владельца не имеет.

Параметр lpName — это имя мьютекса. Если присваивать мьютексу  имя не нужно, то установите данный параметр равным nil. Если  же значение этого  параметра от лично  от nil, то функция выполнит в системе поиск  мьютекса с таким  же именем. При успешном завершении поиска  функция вернет дескриптор найденного мьютекса, в противном случае — дескриптор нового мьютекса.

По завершении использования мьютекса необходимо закрыть его с помощью функции API CloseHandle().

В  листинге 5.6  продемонстрирован  еще  один   способ   синхронизации  потоков,

инициализирующих массив, но на этот раз с использованием мьютексов.

Листинг 5.6. Использование мьютексов для синхронизации потоков

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;

var

MainForm: TMainForm;

implementation

{$R *.DFM}

constMaxSize =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;

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]));

CloseHandle(hMutex);

end;

end;

procedure TMainForm.Button1Click(Sender: TObject);

begin

hMutex := CreateMutex(nil, False, nil);

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

TFooThread.Create(False);

end;

end.Нетрудно заметить, что в этом случае для управления входом  потоков в син хронизируемый блок используется функция WaitForSingleObject(), объяв ленная следующим образом:

function WaitForSingleObject(hHandle: THandle;

dwMilliseconds: DWORD): DWORD; stdcall;

Данная функция предназначена для перевода текущего  потока в состояние ожи дания,  пока объект API, заданный параметром hHandle, не станет доступным. При этом  состояние ожидания может  продлиться вплоть  до истечения интервала вре мени,  заданного в миллисекундах  параметром dwMilliseconds. Термин доступен (signaled) для  различных объектов понимается по разному.  Мьютекс  становится доступным, если он больше  не принадлежит потоку,  в то время  как, например, про цесс становится доступным  после  его завершения. Помимо реального периода вре мени,  параметр dwMilliseconds может  также  иметь  нулевое  значение, которое указывает  на необходимость проверки состояния объекта и немедленный возврат. Возможно и значение INFINITE, указывающее, что ожидать следует до тех пор,  по ка объект не станет доступным. Значения, которые может  возвращать эта функция, перечислены в табл. 5.3.

Таблица 5.3. Константы типа ожидания, используемые функцией API Win32

WaitForSingleObject()

Значение                                   Описание

WAIT_ABANDONED Объект  является  объектом  мьютекса,  но  поток,  владевший этим мьютексом, был завершен до его освобождения. Такой мьютекс   считается покинутым (abandoned),  право   собствен ности  на  его  объект передается вызывающему потоку,  а сам мьютекс становится недоступным

WAIT_OBJECT_0       Состояние объекта определяется как доступное

WAIT_TIMEOUT         Установленный интервал времени ожидания  истек,  и  состоя

ние объекта определяется как недоступное

Повторим еще  раз:  если  мьютекс  не принадлежит какому либо  потоку,  он нахо дится  в доступном  состоянии. Первый же  поток, вызвавший функцию  WaitForS- ingleObject() с запросом на  данный мьютекс, получит  право  собственности на него,  а состояние самого  объекта мьютекса станет недоступным. Когда поток  вызы вает  функцию  ReleaseMutex(), передавая в качестве параметра дескриптор при надлежащего ему мьютекса, право  собственности на этот  мьютекс  у данного потока отбирается, а мьютекс  вновь становится доступным.

НА ЗАМЕТКУ

Помимо функции WaitForSingleObject(), в интерфейсе API Win32 есть также функции WaitForMultipleObjects() и MsgWaitForMultipleObjects(), которые позволяют продлить ожидание до тех пор, пока один или несколько объектов не станут доступными. Эти функции описаны в интерактивной справочной системе интерфейса API Win32.

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

По теме:

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