我正在尝试使用生产者消费者模式来处理和保存一些数据。我正在使用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()?
答案 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();
}