[英]Consumer/Producer with AutoResetEvent
我在 C# 中有以下代碼,用於使用 AutoResetEvent 的消費者和生產者,但它們在有多個生產者和一個消費者的情況下不起作用。 問題是消費者不能消費隊列中的所有項目。 當我調試時,我注意到消費者只能刪除一項,然后返回 false 並且無法再刪除。 似乎問題出在 AutoResetEvent 中,但我不知道出了什么問題。
private AutoResetEvent newItemSignal = new AutoResetEvent(false);
private Queue<Task> iQueue = new Queue<Task>();
public void Enqueue(Task task)
{
lock (((ICollection)iQueue).SyncRoot)
{
iQueue.Enqueue(task);
newItemSignal.Set();
}
}
public bool Dequeue(out Task task, int timeout)
{
if (newItemSignal.WaitOne(timeout, false))
{
lock (((ICollection)iQueue).SyncRoot)
{
task = iQueue.Dequeue();
}
return true;
}
task = default(Task);
return false;
}
使用像這樣的 AutoResetEvent 的問題是您可以調用 Set() 兩次或更多次,但 WaitOne() 只調用一次。 在已發出信號的 ARE 上調用 Set() 將始終失敗,該項目卡在隊列中。 一個標准的線程競爭錯誤。 看起來您可以通過清空消費者中的整個隊列來修復它。 不是真正的解決方案,生產者仍然可以領先於消費者,您只是將幾率降低到每月一次不可調試的階段。
ARE 無法做到這一點,它無法計數。 改用 Semaphore/Slim,它以線程安全的方式進行計數。 或者使用 ConcurrentQueue,添加一個類來解決此類編程問題。
通過使用 AutoResetEvent,您將程序設計為一次只有一個消費者可以消費一個項目。
如果你想堅持類似的設計,你可以改用ManualResetEvent,當任何一個消費者線程發現沒有要消費的項目時重置事件,當生產者線程知道至少有一個項目時設置事件要消耗的物品。
您可以在此處找到 Monitor 類的替代設計
如果您使用的是 .NET 4.0 或更高版本,您還可以使用Blocking 集合
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.