队列<t> .Dequeue返回null </t>

时间:2010-02-12 14:05:00

标签: c# multithreading synchronization

我有一个场景

  • 多个线程正在推送队列上的数据

  • 只有一个线程使用以下代码处理数据

代码 -

  while ( Continue )
  {
        while ( queue.Count > 0 )
        {
             MyObj o = queue.Dequeue();
             someProcess(o);
        }
        myAutoResetEvent.WaitOne();
  }

但有时,queue.Dequeue()在上面的场景中返回null 是什么给了什么?

7 个答案:

答案 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());

否则你应该像其他人所说的那样照顾自己。