ПриМеЧание
В.некоторых.случаях.при.блокировке.потока.из.однопоточного.отделения.(apartment).
возможно.пробуждение.потока.для.обработки.сообщений .Например,.заблокиро-
ванный.поток.может.проснуться.для.обработки.Windows-сообщения,.отправленного.
другим.потоком .Это.делается.для.совместимости.с.моделью.COM .Для.большин-
ства.приложений.это.не.проблема .Но.если.ваш.код.в.процессе.обработки.сообще-
ния.запрет.другой.поток,.может.случиться.взаимная.блокировка .Как.вы.увидите.
в.главе.29,.все.гибридные.блокировки.тоже.могут.вызывать.данные.методы,.так.что.
вышесказанное.верно.и.для.них
В прототипе версий
WaitOne
,
WaitAll
и
SignalAndWait
, не принимающих па-
раметр
timeout
, должно быть указано возвращаемое значение
void
, а не
Boolean
.
В противном случае методы бы всегда возвращали значение
true
из-за предпола-
гаемого бесконечного времени ожидания (
System.Threading.Timeout.Infinite
).
846
Глава.29 .Примитивные.конструкции.синхронизации.потоков
Так что при вызове любого из этих методов нет нужды проверять возвращаемое
им значение.
Как уже упоминалось, классы
AutoResetEvent
,
ManualResetEvent
,
Semaphore
и
Mutex
являются производными от класса
WaitHandle
, то есть наследуют методы
этого класса и их поведение. Впрочем, эти классы обладают и собственными мето-
дами, о которых мы сейчас и поговорим.
Во-первых, конструкторы всех этих классов вызывают Win32-функцию
CreateEvent
(передавая в параметре
bManualReset
значение
FALSE
),
CreateEvent
(передавая в параметре
bManualReset
значение
TRUE
),
CreateSemaphore
или
CreateMutex
. Значение дескриптора, возвращаемого при таких вызовах, сохраняется
в закрытом поле
SafeWaitHandle
, определенном в базовом классе
WaitHandle
.
Во-вторых, классы
EventWaitHandle
,
Semaphore
и
Mutex
предлагают статические
методы
OpenExisting
, вызывающие Win32-функцию
OpenEvent
,
OpenSemaphore
или
OpenMutex
, передавая ей аргумент типа
String
с именем существующего ядра.
Значение дескриптора, возвращаемого при таких вызовах, сохраняется во вновь
созданном объекте, возвращаемым методом
OpenExisting
. При отсутствии ядра
с указанным именем генерируется исключение
WaitHandleCannotBeOpenedExcep-
tion
.
Конструкции режима ядра часто используются для создания приложений, ко-
торые в любой момент времени могут существовать только в одном экземпляре.
Примерами таких приложений являются Microsoft Office Outlook, Windows Live
Messenger, Windows Media Player Windows Media Center. Вот как реализовать такое
приложение:
using System;
using System.Threading;
public static class Program {
public static void Main() {
Boolean createdNew;
// Пытаемся создать объект ядра с указанным именем
using (new Semaphore(0, 1, "SomeUniqueStringIdentifyingMyApp",
out createdNew)) {
if (createdNew) {
// Этот поток создает ядро, так что другие копии приложения
// не могут запускаться. Выполняем остальную часть приложения...
} else {
// Этот поток открывает существующее ядро с тем же именем;
// должна запуститься другая копия приложения.
// Ничего не делаем, ждем возвращения управления от метода Main,
// чтобы завершить вторую копию приложения
}
}
}
}
847
Конструкции.режима.ядра
В этом фрагменте кода используется класс
Semaphore
, но с таким же успехом
можно было воспользоваться классом
EventWaitHandle
или
Mutex
, так как предла-
гаемое объектом поведение не требует синхронизации потока. Однако я использую
преимущество такого поведения при создании объектов ядра. Давайте посмотрим,
как работает показанный код. Представим, что две копии процесса запустились
одновременно. Каждому процессу соответствует его собственный поток, и оба по-
тока попытаются создать объект
Semaphore
с одним и тем же именем (в моем при-
мере
SomeUniqueStringIdentifyingMyApp
). Ядро Windows гарантирует создание
объекта ядра с указанным именем только одним потоком; переменной
createdNew
этого потока будет присвоено значение
true
.
В случае со вторым потоком Windows обнаруживает, что объект ядра с указанным
именем уже существует; соответственно, потоку не позволяется создать еще один
объект. Впрочем, продолжив работу, этот поток может получить доступ к тому же
объекту ядра, что и поток первого процесса. Таким способом потоки из различных
процессов взаимодействуют друг с другом через единое ядро. Но в данном случае
поток второго процесса видит, что его переменной
createdNew
присвоено значение
false
. Таким образом он узнает о том, что первая копия процесса запущена, поэтому
вторая копия немедленно завершает свою работу.
события
События
(events) представляют собой переменные типа
Boolean
, находящиеся
под управлением ядра. Ожидающий события поток блокируется, если оно имеет
значение
false
, и освобождается в случае значения
true
. Существует два вида со-
бытий. Когда событие с автосбросом имеет значение
true
, оно освобождает всего
один заблокированный поток, так как после освобождения первого потока ядро
автоматически возвращает
событию значение
false
. Если же значение
true
имеет
событие с ручным сбросом, оно освобождает все ожидающие этого потоки, так как
в данном случае ядро не присваивает ему значение
false
автоматически, в коде это
должно быть сделано
в явном виде
. Вот как выглядят классы, связанные с событиями:
public class EventWaitHandle : WaitHandle {
public Boolean Set(); // Boolean присваивается true;
// всегда возвращает true
public Boolean Reset(); // Boolean присваивается false;
// всегда возвращает true
}
public sealed class AutoResetEvent : EventWaitHandle {
public AutoResetEvent(Boolean initialState);
}
public sealed class ManualResetEvent : EventWaitHandle {
public ManualResetEvent(Boolean initialState);
}
848
Do'stlaringiz bilan baham: |