КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Использование объектов ядра Windows в управляемом кодеWindows предлагает несколько объектов ядра для выполнения синхронизации потоков: мьютексы, семафоры и события. В этом разделе мы поговорим об использовании этих объектов в CLR. Методы классов Monitor и ReaderWriterLock позволяют выполнять синхронизацию потоков одного домена AppDomain, а объекты ядра можно использовать для синхронизации потоков различных AppDomain или процессов. Поэтому при создании объекта ядра можно задать права доступа, указав, кто и что может делать с этим объектом. Ожидая освобождения объекта ядра, поток должен переходить из пользовательского режима в режим ядра, что сильно снижает производительность. Поэтому синхронизация с использованием объектов ядра — самый медленный из механизмов синхронизации. Он намного медленнее, чем использование класса ReaderWriterLock. Использование объектов ядра в 33 раза медленнее применения методов класса Monitor. В пространстве имен System.Threading есть абстрактный класс WaitHandle. Это довольно простой класс, единственная задача которого — служить оболочкой описателя объекта ядра. В FCL есть несколько классов, производных от WaitHandle. Все классы определены в пространстве имен System.Threading и реализованы в библиотеке MSCorLib.dll. Исключение — класс Semaphore, который реализован в библиотеке System.dll. Иерархия классов выглядит следующим образом:
WaitHandle Mutex Semaphore EventWaitHandle AutoResetEvent ManualResetEvent В базовом классе WaitHandle есть поле, содержащее Win32-описатель объекта ядра. Это поле инициализируется при конструировании класса, производного от WaitHandle. Кроме этого, класс WaitHandle предоставляет открытые методы, единые для всех производных неабстрактных классов. Приведенное ниже псевдо определение класса описывает все интересные открытые методы класса WaitHandle (не показаны некоторые перегруженные версии отдельных методов):
public abstract class WaitHandle : MarshalByRefObject, IDisposable { // Код метода Close вызывает Win32-функцию CloseHandle. public virtual void Close();
// Код метода WaitOne вызывает Win32-функцию WaitForSingleObjectEx. public virtual Boolean WaitOne(); public virtual Boolean WaitOne(Int32 millisecondsTimeout, Boolean exitContext);
// Код метода WaitAny вызывает Win32-функцию WaitForMultipleObjectsEx. public static Int32 WaitAny(WaitHandle[] waitHandles); public static Int32 WaitAny(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext);
// Код метода WaitAll вызывает Win32-функцию WaitForMultipleObjectsEx. public static Boolean WaitAll(WaitHandle[] waitHandles); public static Boolean WaitAll(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext);
// Код метода SignalAndWait вызывает Win32-функцию SignalObjectAndWait. public static Boolean SignalAndWait(WaitHandle toSignal, WaitHandle toWaitOn); public static Boolean SignalAndWait(WaitHandle toSignal, WaitHandle toWaitOn, Int32 millisecondsTimeout, Boolean exitContext);
public const Int32 WaitTimeout = 0x102; }
По поводу этих методов есть несколько замечаний: 1. Можно вызвать метод Close класса WaitHandle (или не имеющий параметров метод Dispose интерфейса IDisposable), чтобы закрыть соответствующий описатель объекта ядра. Внутри этих методов вызывается Win32-функция CloseHandle. 2. Можно вызвать метод WaitOne класса WaitHandle, чтобы вызывающий поток ожидал освобождения соответствующего объекта ядра. Внутри метода вызывается Win32-функция WaitForSingleObjectEx. Если объект освободился, возвращается значение true. В случае тайм-аута возвращается false. 3. Можно вызвать метод WaitAny класса WaitHandle, чтобы вызывающий поток ожидал освобождения одного из объектов ядра, указанных в параметре WaitHandle[]. Возвращаемое значение Int32 является индексом элемента массива, соответствующего освободившемуся объекту ядра или WaitHandleWaitTimeout, если за время тайм-аута ни один объект не освободился. Внутри этого метода вызывается Win32-функция WaitForMultipleObjectsEx, передающая в параметре bWaitAll значение false. 4. Можно вызвать метод WaitAll класса WaitHandle, чтобы заставить вызывающий поток ожидать освобождения всех объектов ядра, указанных в параметре WaitHandle[]. Если все объекты освобождены, возвращается логическое значение true, в противном случае (не все объекты освобождены) — false. Внутри этого метода вызывается Win32-функция WaitForMultipleObjectsEx, передающая в параметре bWaitAll значение true. 5. Статический метод SignalAndWait класса WaitHandle вызывается, чтобы атомарно освободить один объект ядра и ожидать освобождения другого объекта. Если объект освободился, возвращается значение true. В случае тайм-аута возвращается false. Внутри этого метода используется Win32-функции SignalObjectAndWait.
Версии WaitOne, WaitAll и SignalAndWait, которые не принимают параметр тайм-аута, должны прототипироваться как возвращающие void, а не Boolean. В противном случае эти методы будут возвращать только значение true, поскольку по умолчанию время ожидания не ограничено. При вызове любого из этих методов проверять возвращаемое значение не нужно. Версии WaitOne, WaitAll, WaitAny и SignalAndWait, не принимающие параметр exitContext, подставляют в него значение по умолчанию —false. CLR позволяет разместить объект в собственном контексте, применив к классу следующие методы: 1. System.Runtime.Remotin.Contexts.ContextAttribute 2. System.Runtime.Remoting.Contexts.SynchronizationAttribute. Контекст можно сравнить с мини-доменом AppDomain или мини-окружением для экземпляров класса. В любом случае эти атрибуты имеют отношение к удаленному доступу, а использование этих функций сейчас не рекомендуется. При вызове одного из методов класса WaitHandle, принимающего параметр exitContext, обычно передают значение false. Классы Mutex, Semaphore, AutoResetEvent и ManualResetEvent являются производными от WaitHandle, поэтому наследуют поведение этого родительского класса. В этих классах появились некоторые дополнительные методы, о которых мы сейчас поговорим. Во-первых, код конструкторов всех этих классов вызывает Win32-функции CreateMutex, CreateSemaphore, CreateEvent (передавая false в параметре bManualReset) или CreateEvent (передавая true в параметре bManualReset). Возвращаемый всеми вызовами описатель сохраняется в закрытом поле, определенном в базовом классе WaitHandle. Во-вторых, в классах Mutex, Semaphore и EventWaitHandle есть статические методы OpenExisting, их внутренний код вызывает Win32- функции OpenMutex, OpenSemaphore или OpenEvent, передавая аргумент string, в котором указывается существующий именованный объект ядра. Возвращаемый всеми этими методами описатель сохраняется в новом объекте, который и возвращает метод OpenExisting. Если объекта ядра с указанным именем не существует, генерируется исключение WaitHandleCannotBeOpenedException. В-третьих, чтобы освободить объект ядра Mutex или Semaphore, нужно вызвать метод ReleaseMutex из класса Mutex или Release — из класса Semaphore. Чтобы установить или сбросить событие объекта ядра с автоматическим или ручным сбросом, нужно вызывать (унаследованные от EventWaitHandle) методы Set или Reset класса AutoResetEvent или ManualResetEvent. Прототипы методов Set или Reset класса EventWaitHandle определяют в качестве возвращаемого типа Boolean, но они всегда возвращают значение true, поэтому нет необходимости писать код, проверяющий возвращаемое этими методами значение.
|