C#生产者/消费者实施问题

时间:2012-11-28 16:13:01

标签: c#

我有一个生产者 - 消费者类,如下所示。

public class ProducerConsumer<T> where T : class
    {
        private Thread _workerThread;
        private readonly Queue<T> _workQueue;
        private readonly object _lockObject = new object();
        private readonly Action<T> _workCallbackAction;
        private ManualResetEvent _workerWaitSignal;

        public ProducerConsumer(Action<T> action)
        {               
            _workCallbackAction = action;
            _workQueue = new Queue<T>();                
        }

        private void DoWork()
        {
            while (true)
            {
                T workItemToBeProcessed = default(T);
                bool hasSomeWorkItem = false;

                lock (_lockObject)
                {
                    hasSomeWorkItem = _workQueue.Count > 0;

                    if (hasSomeWorkItem)
                    {
                        workItemToBeProcessed = _workQueue.Dequeue();
                        if (workItemToBeProcessed == null)
                        {
                            return;
                        }
                    }
                }
                if (hasSomeWorkItem)
                {
                    if (_workCallbackAction != null)
                    {
                        _workCallbackAction(workItemToBeProcessed);
                    }
                }
                else
                {
                    _workerWaitSignal.WaitOne();
                    Debug.WriteLine("Waiting for signal.");
                }
            }
        }

        public void EnQueueWorkItem(T workItem)
        {
            lock (_lockObject)
            {
                _workQueue.Enqueue(workItem);
                _workerWaitSignal.Set();
            }
        }

        public void StopWork(ManualResetEvent stopSignal)
        {
            EnQueueWorkItem(null);
            _workerThread.Join();
            _workerWaitSignal.Close();
            _workerWaitSignal = null;
            if (stopSignal != null)
            {
                stopSignal.Set();
            }
        }

        public void ReStart()
        {
            _workerWaitSignal = new ManualResetEvent(false);
            _workerThread = new Thread(DoWork) { IsBackground = true };
            _workerThread.Start();
        }
    }

我正在以下列方式使用它:

 public partial class Form1 : Form
    {

        private RecordProducerConsumer<string> _proConsumer;
        public Form1()
        {
            InitializeComponent();
            _proConsumer = new RecordProducerConsumer<string>(DoAction);
        }


        private bool restart=true;
        private int item = 0;

        private void button1_Click(object sender, EventArgs e)
        {

            if (restart)
            {
                _proConsumer.ReStart();
                restart = false;
            }

            item++;
            _proConsumer.EnQueueWorkItem(item.ToString());

        }

        private void DoAction(string str)
        {
            Debug.WriteLine(str);
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            ManualResetEvent mre = new ManualResetEvent(false);
            _proConsumer.StopWork(mre);
            mre.WaitOne();
            restart = true;
        }

        private void Stop(ManualResetEvent mre)
        {
            mre.WaitOne();          
        }
    }

我的问题或我无法理解的是,当我点击Start按钮时,我只添加了一个项目并Dequeue该项目,但继续运行循环,所以我看到很多{{1在"Waiting for signal."的{​​{1}}窗口打印输出。

为什么它不会停留在Output Visual Studio方法中,为什么它一直在运行?

3 个答案:

答案 0 :(得分:0)

几个问题:

  1. 在执行等待之后输出'等待问题'几乎没有任何意义。考虑在实际等待之前移动写
  2. 您正在使用ManualResetEvent - 正如其名称所示,它需要手动重置才能从信号状态恢复。但是,我无法在您的代码中看到Reset的来电。
  3. 为了避免其他并发问题(例如,当其他线程在将其他工作项排入队列后设置事件时重置事件时的竞争条件),请考虑使用Semaphore代替您的方案。

答案 1 :(得分:0)

试试这个......我可能错了......但这就是我通过阅读你的代码可以搞清楚的。希望这会有所帮助:)

 private void button1_Click(object sender, EventArgs e)
    {

        if (restart)
        {
            restart = false;
            _proConsumer.ReStart();                
        }

        item++;
        _proConsumer.EnQueueWorkItem(item.ToString());

    }

答案 2 :(得分:0)

我还没有彻底阅读这些代码,但我可以猜测你打算使用AutoResetEvent(在某些WaitOne()发布后自动重置)而不是{{ 3}}(在您明确调用Reset()之前保持设置状态。)

另外,您有没有理由不使用.NET ManualResetEvent?它是生产者/消费者模式的框架实现,它运作良好。