Классы Semaphore и SemaphoreSlim
System.Threading.Semaphore Класс представляет собой именованный (общесистемный) или локальный семафор. Он является тонкой оболочкой вокруг объекта семафора Win32. Семафоры Win32 являются семафорами счета, которые могут быть использованы для управления доступом к пулу ресурсов.
SemaphoreSlim Класс представляет упрощенный, быстрый семафор, который можно использовать для ожидания внутри одного процесса, когда предполагается, что времена ожидания будут очень короткими. SemaphoreSlimиспользует максимально примитивы синхронизации, предоставляемые общеязыковой среды выполнения (CLR). Тем не менее, он также предоставляет неактивно инициализированные дескрипторы ожидания на основе ядра при необходимости поддержки ожидания для нескольких семафоров. SemaphoreSlimтакже поддерживает использование токенов отмены, но не поддерживает именованные семафоры или использование дескриптора ожидания для синхронизации.
Управление ограниченным ресурсом
Потоки входят в семафор посредством вызова метода WaitOne, который наследуется от класса WaitHandle, в случае объекта System.Threading.Semaphore либо метода SemaphoreSlim.Wait или SemaphoreSlim.WaitAsync в случае объекта SemaphoreSlim. По возвращении вызова счетчик на семафоре уменьшается на единицу. При запросе потоком записи счетчик равен нулю, поток блокируется. Потоки освобождают семафор посредством вызова метода Semaphore.Release или SemaphoreSlim.Release, заблокированные потоки разрешено вводить. Для входа блокированных потоков в семафор нет гарантированного порядка, например first-in, first-out (FIFO) или последним поступил — первым обслужен (LIFO).
Поток может ввести семафор несколько раз, вызвав System.Threading.Semaphore метод объекта WaitOne или SemaphoreSlim метод объекта Wait многократно. Чтобы освободить семафор, поток может либо вызвать метод перегрузки Semaphore.Release() или SemaphoreSlim.Release() одинаковое количество раз, либо вызвать метод перегрузки Semaphore.Release(Int32) или SemaphoreSlim.Release(Int32) и указать количество освобождаемых записей.
Семафоры и идентификация потоков
Типы два семафора не обеспечивают идентификацию потоков по вызовам методов WaitOne, Wait, Release и SemaphoreSlim.Release. Например, обычным сценарием использования семафора является наличие потока производителя и потока получателя. При этом один поток всегда увеличивает счетчик семафора, а другой всегда уменьшает его.
Программист должен обеспечить, чтобы поток не освобождал семафор слишком много раз. Например предположим, что семафор имеет максимальное значение счетчика равное двум, а два потока A и B входят в семафор. Если ошибка программирования в потоке B приводит к тому, что она вызывается Release
дважды, оба вызова успешно выполнены. Счетчик на семафоре переполнен, и если поток A вызывает Release
, SemaphoreFullException создается исключение.
Именованные семафоры
Операционная система Windows позволяет присваивать семафорам имена. Именованный семафор относится ко всей системе. То есть после создания именованный семафор становится видимым для всех потоков во всех процессах. Таким образом именованный семафор может использоваться для синхронизации действий процессов, а также потоков.
Можно создать Semaphore объект, который представляет именованный системный семафор с помощью одного из конструкторов, указывающих имя.
Примечание.
Так как именованные семафоры относятся ко всей системе, можно иметь несколько объектов Semaphore, представляющих один и тот же именованный семафор. При каждом вызове конструктора или метода Semaphore.OpenExisting создается новый объект Semaphore. Если указать одно и то же имя несколько раз, создается несколько объектов, представляющих один и тот же именованный семафор.
Будьте осторожны при использовании именованных семафоров. Так как они относятся ко всей системе, другой процесс, использующий то же имя, может неожиданно войти в семафор. Вредоносный код, выполняемый на одном компьютере может использовать это как основу для атак типа "отказ в обслуживании".
Используйте безопасность управления доступом для защиты объекта Semaphore, представляющего именованный семафор, предпочтительнее с помощью конструктора, который определяет объект System.Security.AccessControl.SemaphoreSecurity. Также можно применить безопасность управления доступом с помощью метода Semaphore.SetAccessControl, однако это оставит брешь в защите между временем создания семафора и временем, когда он будет защищен. Защита семафоров с помощью безопасности управления доступом способствует предотвращению атак злоумышленников, но не решает проблемы непреднамеренного конфликта имен.