使用信号量的C#Producer-Consumer

时间:2017-03-27 14:08:48

标签: c# multithreading concurrency producer-consumer

受到信号量小书的启发,我决定使用信号量实现Producer-Consumer问题。

我特别希望能够随意停止所有工作线程。 我已经广泛地测试了我的方法,并且找不到任何有问题的东西。

以下代码是测试的原型,可以作为控制台应用程序运行:

using System;
using System.Collections.Concurrent;
using System.Threading;
using NUnit.Framework;

public class ProducerConsumer
{
    private static readonly int _numThreads = 5;
    private static readonly int _numItemsEnqueued = 10;
    private static readonly Semaphore _workItems = new Semaphore(0, int.MaxValue);
    private static readonly ManualResetEvent _stop = new ManualResetEvent(false);
    private static ConcurrentQueue<int> _queue;

    public static void Main()
    {
        _queue = new ConcurrentQueue<int>();

        // Create and start threads.
        for (int i = 1; i <= _numThreads; i++)
        {
            Thread t = new Thread(new ParameterizedThreadStart(Worker));

            // Start the thread, passing the number.
            t.Start(i);
        }

        // Wait for half a second, to allow all the
        // threads to start and to block on the semaphore.
        Thread.Sleep(500);

        Console.WriteLine(string.Format("Main thread adds {0} items to the queue and calls Release() {0} times.", _numItemsEnqueued));
        for (int i = 1; i <= _numItemsEnqueued; i++)
        {
            Console.WriteLine("Waking up a worker thread.");
            _queue.Enqueue(i);
            _workItems.Release(); //wake up 1 worker
            Thread.Sleep(2000); //sleep 2 sec so it's clear the threads get unblocked 1 by 1
        }

        // sleep for 5 seconds to allow threads to exit
        Thread.Sleep(5000);
        Assert.True(_queue.Count == 0);

        Console.WriteLine("Main thread stops all threads.");
        _stop.Set();

        // wait a while to exit
        Thread.Sleep(5000);
        Console.WriteLine("Main thread exits.");
        Console.WriteLine(string.Format("Last value of Semaphore was {0}.", _workItems.Release()));
        Assert.True(_queue.Count == 0);
        Console.WriteLine("Press Enter to exit.");
        Console.ReadLine();
    }

    private static void Worker(object num)
    {
        // Each worker thread begins by requesting the semaphore.
        Console.WriteLine("Thread {0} begins and waits for the semaphore.", num);
        WaitHandle[] wait = { _workItems, _stop };
        int signal;
        while (0 == (signal = WaitHandle.WaitAny(wait)))
        {
            Console.WriteLine("Thread {0} becomes unblocked by Release() and has work to do.", num);
            int res;
            if (_queue.TryDequeue(out res))
            {
                Console.WriteLine("Thread {0} dequeues {1}.", num, res);
            }
            else
            {
                throw new Exception("this should not happen.");
            }
        }

        if (signal == 1)
            Console.WriteLine("Thread {0} was stopped.", num);

        Console.WriteLine("Thread {0} exits.", num);
    }
}

现在对于我的问题,我假设当我在信号量上调用WaitHandle.WaitAny(semaphore)时,我只使用Release()将被唤醒。但是,我在文档中找不到这确实是真的。任何人都可以证实这是真的吗?

1 个答案:

答案 0 :(得分:2)

确实很有意思的是,文档似乎没有明确说明在WaitOne的情况下只有1个线程会收到信号。当你熟悉多线程理论时,这就变得不言而喻了。

是的,WaitOne上调用的Semaphore(以及包含WaitAny的{​​{1}}列表中调用的WaitHandle)一个线程。如果您希望从MSDN引用,那么SemaphoreSemaphore的子类,which is:

  

封装等待独占访问共享资源的特定于操作系统的对象。

是的,除非明确声明的方法提供独占访问权。

例如,WaitHandle的方法WaitOne将为所有等待的帖子but documentation is explicit about it取消阻止:

  

通知一个或多个等待线程发生了一个事件。