Как проверить состояние семафора - PullRequest
15 голосов
/ 07 сентября 2011

Я хочу проверить состояние Semaphore, чтобы увидеть, сигнализируется ли он или нет (поэтому, если сигнализируется t, я могу отпустить его). Как я могу это сделать?

EDIT1:

У меня есть два потока, один будет ждать семафора, а другой должен выпустить Semaphore. Проблема в том, что второй поток может вызывать Release() несколько раз, когда первый поток не ожидает его. Таким образом, второй поток должен обнаружить, что если он вызывает Release(), он генерирует какую-либо ошибку или нет (он генерирует ошибку, если вы пытаетесь выпустить семафор, если никто не ждет его). Как я могу это сделать? Я знаю, что могу использовать флаг, чтобы сделать это, но это ужасно. Есть ли лучший способ?

Ответы [ 4 ]

12 голосов
/ 07 сентября 2011

Вы можете проверить, сигнализируется ли Semaphore, вызвав WaitOne и передав значение тайм-аута 0 в качестве параметра.Это приведет к немедленному возвращению WaitOne со значением true или false, указывающим, был ли семафор сигнализирован.Это, конечно, может изменить состояние семафора, что делает его громоздким в использовании.

Еще одна причина, по которой этот трюк вам не поможет, заключается в том, что семафор считается сигнальным, когда хотя бы один счетчик доступен,Похоже, вы хотите знать, когда у семафора есть все доступные отсчеты.Класс Semaphore не имеет такой точной способности.Вы можете использовать возвращаемое значение из Release, чтобы вывести, что это за счет, но это заставит семафор изменить свое состояние и, конечно, он все равно выдаст исключение, если у семафора уже были все счетчики до выполнения вызова.

Нам нужен семафор с операцией освобождения, которая не выбрасывает.Это не очень сложно.Приведенный ниже метод TryRelease вернет значение true, если счет стал доступным, или значение false, если семафор уже был на maximumCount.В любом случае это никогда не вызовет исключения.

public class Semaphore
{
    private int count = 0;
    private int limit = 0;
    private object locker = new object();

    public Semaphore(int initialCount, int maximumCount)
    {
        count = initialCount;
        limit = maximumCount;
    }

    public void Wait()
    {
        lock (locker)
        {
            while (count == 0) 
            {
                Monitor.Wait(locker);
            }
            count--;
        }
    }

    public bool TryRelease()
    {
        lock (locker)
        {
            if (count < limit)
            {
                count++;
                Monitor.PulseAll(locker);
                return true;
            }
            return false;
        }
    }
}
7 голосов
/ 07 сентября 2011

Похоже, вам нужен другой объект синхронизации, потому что Семафор не предоставляет таких функций для проверки того, сигнализируется ли он в определенный момент времени.

Семафор позволяет автоматически запускать кодкоторый ожидает сигнального состояния, используя, например, WaitOne()/Release() методы.

Вы можете взглянуть на новый .NET 4 class SemaphoreSlim , который предоставляет свойство CurrentCount , возможно, выможет использовать его.

CurrentCount
Получает число потоков, которым будет разрешено входить в SemaphoreSlim.

РЕДАКТИРОВАТЬ: Обновлено из-за обновленного вопроса

В качестве быстрого решения вы можете обернуть semaphore.Release() с помощью try / catch и обрабатывать SemaphoreFullException, работает ли оно так, как вы ожидали?

Использование SemaphoreSlim Вы можете проверить CurrentCount таким образом:

 int maxCount = 5;
 SemaphoreSlim slim = new SemaphoreSlim(5, maxCount);            

 if (slim.CurrentCount == maxCount)
 {
    // generate error
 }
 else
 {
   slim.Release();
 }
4 голосов
/ 07 сентября 2011

Способ реализации семафора с использованием сигнализации заключается в следующем. Не имеет смысла запрашивать состояние за пределами этого, поскольку это не будет потокобезопасным.

Создание экземпляра с maxThreads слотами, изначально все доступно:

var threadLimit = new Semaphore(maxThreads, maxThreads);

Используйте следующее для ожидания (блокировки) для свободного слота (если maxThreads уже занято):

threadLimit.WaitOne();

Используйте следующее, чтобы освободить слот:

threadLimit.Release(1);

Вот полный пример здесь .

0 голосов
/ 08 января 2013

Полезно знать, когда все значения доступны в семафоре.Я использовал следующую логику / решение.Я делюсь здесь, потому что я не видел других решений, решающих эту проблему.

//List to add a variable number of handles
private List<WaitHandle> waitHandles;

//Using a mutex to make sure that only one thread/process enters this section
using (new Mutex(....))
{
    waitHandles = new List<WaitHandle>();
    int x = [Maximum number of slots available in the semaphore];

    //In this for loop we spin a thread per each slot of the semaphore
    //The idea is to consume all the slots in this process 
    //not allowing anything else to enter the code protected by the semaphore
    for (int i = 0; i < x; i++)
    {
        Thread t = new Thread(new ParameterizedThreadStart(TWorker));
        ManualResetEvent mre = new ManualResetEvent(false);
        waitHandles.Add(mre);
        t.Start(mre);
    }

    WaitHandle.WaitAll(waitHandles.ToArray());

    [... do stuff here, all semaphore slots are blocked now ...]

    //Release all slots
    semaphore.Release(x);
}

private void TWorker(object sObject)
{
    ManualResetEvent mre = (ManualResetEvent)sObject;
    //This is an static Semaphore declared and instantiated somewhere else
    semaphore.WaitOne();
    mre.Set();
}
...