使用AutoResetEvent生产者消费者

时间:2009-12-10 18:12:31

标签: c# multithreading waithandle

我正在尝试使用生产者消费者模式来处理和保存一些数据。我正在使用AutoResetEvent在两个therads之间发送信号,这是我的代码

这是生产者功能

 public Results[] Evaluate()
    {
        processingComplete = false;
        resultQueue.Clear();
        for (int i = 0; i < data.Length; ++i)
            {
                if (saveThread.ThreadState == ThreadState.Unstarted)
                    saveThread.Start();
               //-....
               //Process data 
               // 
                lock (lockobject)
                {
                    resultQueue.Enqueue(result);
                }

                signal.Set();
            }
            processingComplete = true;
        }

这是消费者功能

   private void SaveResults()
    {
        Model dataAccess = new Model();

        while (!processingComplete || resultQueue.Count > 0)
        {
            if (resultQueue.Count == 0)
                signal.WaitOne();
            ModelResults result;
            lock (lockobject)

            {
                result = resultQueue.Dequeue();
            }
            dataAccess.Save(result);
        }
        SaveCompleteSignal.Set();
    }

所以我的问题有时是resultQueue.Dequeue()抛出InvalidOperation异常,因为Queue为空。我不确定我做错了什么不应该上面的那个阻止队列为空的信号.WaitOne()?

3 个答案:

答案 0 :(得分:2)

由于缺少正确的锁定,您会遇到同步问题。

您应该锁定所有队列访问权限,包括计数检查。

此外,以这种方式使用Thread.ThreadState是一个“坏主意”。从ThreadState的MSDN文档:

  

“线程状态仅对调试方案感兴趣。您的代码永远不应使用线程状态来同步线程的活动。”

您不能依赖此作为处理同步的方法。您应该重新设计以确保线程在使用之前启动。如果它没有启动,只是不要初始化它。 (你总是可以使用空检查 - 如果线程为null,则创建它并启动它。)

答案 1 :(得分:1)

在同步上下文之外检查队列的计数。由于Queue不是线程安全的,这可能是一个问题(可能是Enqueue正在进程计数返回1但没有项目可以出列),如果你要使用多个消费者,那将严重错误。

您可能想阅读Joseph Albahari撰写的篇章文章,他还有good sample for your problem以及a "better" solution without OS synchronization objects

答案 2 :(得分:0)

您必须将lock()置于队列的所有引用周围。您还有一些关于识别处理完成的问题(在队列末尾您将收到信号,但队列将为空)。

public Results[] Evaluate()
{
    processingComplete = false;
    lock(lockobject) 
    {
        resultQueue.Clear();
    }
    for (int i = 0; i < data.Length; ++i)
    {
        if (saveThread.ThreadState == ThreadState.Unstarted)
            saveThread.Start();
       //-....
       //Process data 
       // 
        lock (lockobject)
        {
            resultQueue.Enqueue(result);
        }

        signal.Set();
    }
    processingComplete = true;
}

private void SaveResults()
{
    Model dataAccess = new Model();

    while (true)
    {
        int count;

        lock(lockobject)  
        {
            count = resultQueue.Count;
        }
        if (count == 0)
            signal.WaitOne();

        lock(lockobject)  
        {
            count = resultQueue.Count;
        }
        // we got a signal, but queue is empty, processing is complete
        if (count == 0)
            break;

        ModelResults result;
        lock (lockobject)
        {
            result = resultQueue.Dequeue();
        }
        dataAccess.Save(result);
    }
    SaveCompleteSignal.Set();
}
相关问题