队列<T> .Dequeue返回null

Kum*_*mar 7 c# multithreading synchronization

我有一个场景

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

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

代码 -

  while ( Continue )
  {
        while ( queue.Count > 0 )
        {
             MyObj o = queue.Dequeue();
             someProcess(o);
        }
        myAutoResetEvent.WaitOne();
  }
Run Code Online (Sandbox Code Playgroud)

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

Guf*_*ffa 8

您需要同步对队列的访问.lock在所有访问队列的代码段(包括读取和写入)周围放置语句.如果从多个线程同时访问队列,内部结构可能已损坏,几乎任何事情都可能发生.

  • @Kumar:有两个编写器(在不同的线程上)添加到一个本身不是线程安全的集合会导致问题.Guffa是对的; 你需要同步访问.请参阅我的答案,以获得一种随意的解释. (2认同)

Dan*_*ker 8

你需要阅读这篇博文.

此外,这里是一个非常小的"通道"骨架,用于线程之间的通信:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*Tao 8

你说:

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

Queue<T>.Enqueue方法不是线程安全的.这意味着如果多个线程正在调用它,则需要在需要同步的方法完成工作Enqueue.一个简单的例子就是更新Count属性.这是一个安全的赌注,在Enqueue方法的某个地方有一行看起来像这样:

++count;
Run Code Online (Sandbox Code Playgroud)

但众所周知,这不是原子操作.它真的更像是这样(就实际发生的情况而言):

int newCount = count + 1;
count = newCount;
Run Code Online (Sandbox Code Playgroud)

所以说当前count是5,而线程1已经过去int newCount = count + 1......然后线程1认为,"好吧,现在计数是5,所以我会把它变成6".但是下一个执行的操作是线程2到达int newCount = count + 1并且认为与线程1相同的事情("计数现在是6").因此,刚刚将两个项目添加到队列中,但计数仅从5到6.

这只是一个非常基本的例子,说明Queue<T>.Enqueue当访问未同步时非线程安全方法如何搞砸了.它没有具体解释你的问题中发生了什么; 我的目的只是指出你所做的不是线程安全的,会导致意外的行为.


Jud*_*ngo 5

Guffa是正确的,因为Queue <T>不是线程安全的,所以有多个线程对队列进行读写会导致问题。

如果您使用的是.NET 4,请使用ConcurrentQueue <T>类,该类是线程安全的。如果您不在.NET 3或更早版本上,则可以按照Guffa的说明进行自己的锁定,也可以使用第3方库。