我有一个场景
多个线程正在推送队列上的数据
只有一个线程使用以下代码处理数据
代码 -
while ( Continue )
{
while ( queue.Count > 0 )
{
MyObj o = queue.Dequeue();
someProcess(o);
}
myAutoResetEvent.WaitOne();
}
但有时,queue.Dequeue()在上面的场景中返回null 是什么给了什么?
答案 0 :(得分:8)
您需要同步对队列的访问权限。在访问队列的所有代码段(包括读取和写入)周围放置lock
语句。如果从多个线程同时访问队列,内部结构可能已损坏,几乎任何事情都可能发生。
答案 1 :(得分:8)
您需要阅读this blog post。
此外,这里是一个非常小的“通道”骨架,用于线程之间的通信:
public class Channel<T>
{
private readonly Queue<T> _queue = new Queue<T>();
public void Enqueue(T item)
{
lock (_queue)
{
_queue.Enqueue(item);
if (_queue.Count == 1)
Monitor.PulseAll(_queue);
}
}
public T Dequeue()
{
lock (_queue)
{
while (_queue.Count == 0)
Monitor.Wait(_queue);
return _queue.Dequeue();
}
}
}
答案 2 :(得分:8)
你说:
多个线程正在推送队列上的数据
Queue<T>.Enqueue
方法不是线程安全的。这意味着工作在 Enqueue
方法中完成,如果多个线程正在调用它,则需要同步。一个简单的例子是更新Count
属性。可以肯定的是,在Enqueue
方法的某个地方有一条看起来像这样的行:
++count;
但众所周知,这不是原子操作。它真的更像是这样(就实际发生的事情而言):
int newCount = count + 1;
count = newCount;
所以说count
目前是5,而线程1超过int newCount = count + 1
...然后线程1认为,“好吧,计数现在是5,所以我会把它变成6。 “但是下一个执行的操作是线程2到达int newCount = count + 1
并且认为与线程1相同的事情(“计数现在是6”)。因此,刚刚将两个项目添加到队列中,但计数仅从5变为6。
这只是一个非常基本的例子,说明当访问未同步时,像Queue<T>.Enqueue
这样的非线程安全方法会如何搞乱。它没有具体解释你的问题中发生了什么;我的目的只是指出你正在做的事情不是线程安全的,会导致意想不到的行为。
答案 3 :(得分:4)
Guffa是正确的,有多个线程读取和写入队列将导致问题,因为Queue&lt; T&gt;不是线程安全的。
如果您使用的是.NET 4,请使用线程安全的ConcurrentQueue<T>类。如果你不是在.NET 3或更早版本,你可以像Guffa指出的那样进行自己的锁定,或者使用第三方库。
答案 4 :(得分:2)
确保没有任何内容将null
值推入队列。允许null
作为排队值。另外,根据this document,只有Queue<T>
的静态成员是线程安全的,所以要注意跨线程进行读写。
答案 5 :(得分:0)
你为什么不这样解决问题?
while (IsRunning)
{
do
{
MyObj myObj = queue.Dequeue();
if (myObj != null)
{
DoSomethingWith(myObj);
}
} while (myObj != null);
myAutoResetEvent.WaitOne();
}
好的,在阅读了Earwickers评论和所有其他答案之后,你没事,我只是假的。所以请不要在多线程上下文中使用上面的代码。
答案 6 :(得分:0)
如果您碰巧使用非通用队列(不是我建议使用它),您可以使用Queue.Synchronized方法获取线程安全的包装器:
Queue queue = Queue.Synchronized(new Queue());
否则你应该像其他人所说的那样照顾自己。