使用信号量的C#生产者-消费者 [英] C# Producer-Consumer using Semaphores

查看:74
本文介绍了使用信号量的C#生产者-消费者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

受《信号量小书》的启发,我决定使用信号量实现生产者-消费者问题.

Inspired by The Little Book of Semaphores, I decided to implement the Producer-Consumer problem using Semaphores.

我特别希望能够随意停止所有Worker线程.我已经对方法论进行了广泛的测试,没有发现任何问题.

I specifically want to be able to stop all Worker threads at will. I've tested my methodolodgy extensively and can't find anything faulty.

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

Following code is a prototype for testing and can be ran as a Console application:

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);
    }
}

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

Now for my question, I'm using WaitHandle.WaitAny(semaphore) under the assumption that when I call Release() on the semaphore, only 1 Worker will be woken up. However, I can't find reassurance in the documentation that this is actually true. Can anyone confirm this is true?

推荐答案

确实有趣的是,文档似乎没有明确指出,在 WaitOne 的情况下,只会收到1个线程信号.当您熟悉多线程理论时,这显然是不言而喻的.

It is indeed interesting that it seems that the documentation doesn't state explicitly that in the case of WaitOne only 1 thread will receive the signal. When you get familiar with multithreading theory this becomes somewhat self-evident.

是的,在 Semaphore 上调用的 WaitOne (和在 WaitHandle 列表上调用的 WaitAny 包含 Semaphore 的代码被单个线程接收.如果要从MSDN引用,则为 Semaphore WaitHandle

Yes, WaitOne that is called on Semaphore (and WaitAny that is called on a list of WaitHandles that include Semaphore) is received by a single thread. If you want reference from MSDN so here it is, Semaphore is child class of WaitHandle, which is:

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

是的,除非明确声明的方法提供互斥访问.

So yes, unless explicitly stated methods provide exclusive access.

例如 ManualResetEvent 的示例方法 WaitOne 将取消所有等待线程的阻塞,

For example method WaitOne of ManualResetEvent will unblock for all waiting threads, but documentation is explicit about it:

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

这篇关于使用信号量的C#生产者-消费者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆