线程安全的有限大小队列
Posted
技术标签:
【中文标题】线程安全的有限大小队列【英文标题】:Thread safe limited size queue 【发布时间】:2015-09-07 09:29:22 【问题描述】:我正在尝试编写一个 subj 队列,但我遇到了死锁和其他多线程问题。我想使用Interlocked.CompareExchange
来避免使用lock
。但是这段代码没有按预期工作:它只是擦除整个队列。我在这里做错了什么?
public class FixedSizedQueue<T> : IEnumerable<T>
readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
public int Limit get; set;
public FixedSizedQueue(int limit)
Limit = limit;
public void Enqueue(T obj)
_queue.Enqueue(obj);
if (_queue.Count <= Limit)
return;
int count = _queue.Count;
if (_queue.Count != Interlocked.CompareExchange(ref count, count, _queue.Count))
T overflow;
while (_queue.TryDequeue(out overflow))
public T[] ToArray()
return _queue.ToArray();
public IEnumerator<T> GetEnumerator()
return _queue.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
也许我只需要另一个线程来切断队列......
【问题讨论】:
好吧,您的 while 循环清楚地擦除了整个队列。那是行不通的。限制那个循环。 引用Eric Lippert:“就拿锁吧。” @Corak tnx 链接,我遵循这个建议 使用 BlockingCollection 代替,BoundedCapacity 属性设置限制。它在底层使用 ConcurrentQueue 来实现集合。 【参考方案1】:Interlocked.CompareExchange
在堆栈变量count
上毫无意义,因为它是从单线程访问的。正如我猜想的那样,您尝试在_queue.Count
上使用此方法,但由于.Count
是一个属性,而不是一个简单的变量,因此编译失败。所以你需要在你的类中定义计数器。
public class FixedSizedQueue<T> : IEnumerable<T>
readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
int CountShadow = 0; // Counter for check constraints.
public int Limit get; set;
public FixedSizedQueue(int limit)
Limit = limit;
public void Enqueue(T obj)
/* Update shadow counter first for check constraints. */
int count = CountShadow;
while(true)
if(count => Limit) return; // Adding element would violate constraint
int countOld = Interlocked.CompareExchange(ref CountShadow, count, count + 1);
if(countOld == count) break; //Successful update
count = countOld;
_queue.Enqueue(obj); // This will update real counter.
...
另外,您需要为Limit
属性设置自己的设置器,这将保持不变的CountShadow <= Limit
。或者只是禁止用户在对象构造后设置该属性。
【讨论】:
以上是关于线程安全的有限大小队列的主要内容,如果未能解决你的问题,请参考以下文章