Как правильно ждать семафор?

Я думал, что следующий код позволит запустить все 10 потоков, по два за раз, а затем вывести "done" после Release() называется 10 раз. Но это не то, что случилось:

        int count = 0;

        Semaphore s = new Semaphore(2, 2);
        for (int x = 0; x < 10; x++)
        {
            Thread t = new Thread(new ThreadStart(delegate()
            {
                s.WaitOne();
                Thread.Sleep(1000);
                Interlocked.Increment(ref count);                       
                s.Release();
            }));
            t.Start(x);
        }

        WaitHandle.WaitAll(new WaitHandle[] { s });
        Console.WriteLine("done: {0}", count);

выход:

done: 6

Если единственный способ реализовать функциональность, которую я ищу, это передать EventWaitHandle к каждой теме, а затем сделать WaitAll() на массиве тех EventWaitHandlesтогда какой смысл делать WaitAll() на массиве только семафор? Другими словами, когда ожидающий поток разблокируется?

2 ответа

Решение

WaitHandle.WaitAll(new WaitHandle[] { s }); ждет так же, как s.WaitOne();, Входит при первой возможности. Похоже, вы ожидаете, что этот вызов будет ждать всех других операций с семафорами, но операционная система не сможет определить разницу. Эта команда вполне может быть первой, которой предоставлен доступ к семафору.

Я думаю, что вам нужно это Barrier учебный класс. Это сделано для параллельности в стиле fork-join.

WaitHandle.WaitAll просто ждет, пока все обработчики не будут в сигнальном состоянии.

Поэтому, когда вы звоните WaitHandle.WaitAll на одном WaitHandle работает так же, как вы звоните s.WaitOne()

Например, вы можете использовать следующий код для ожидания всех запущенных потоков, но разрешить параллельную работу двух потоков:

int count = 0;

Semaphore s = new Semaphore(2, 2);
AutoResetEvent[] waitHandles = new AutoResetEvent[10];
for (int x = 0; x < 10; x++)
    waitHandles[x] = new AutoResetEvent(false);

for (int x = 0; x < 10; x++)
{
    Thread t = new Thread(threadNumber =>
        {
            s.WaitOne();
            Thread.Sleep(1000);
            Interlocked.Increment(ref count);
            waitHandles[(int)threadNumber].Set();
            s.Release();
        });
    t.Start(x);
}

WaitHandle.WaitAll(waitHandles);
Console.WriteLine("done: {0}", count);
Другие вопросы по тегам