多线程.NET队列问题

本文关键字:问题 队列 NET 多线程 | 更新日期: 2023-09-27 17:59:47

我的代码中有一个wierd错误。这种情况极为罕见(可能每几周发生一次),但它确实存在,我不知道为什么。

我们有2个线程在运行,1个线程获取网络消息并将它们添加到队列中,如下所示:

DataMessages.Enqueue(new DataMessage(client, msg));

另一个线程从这个队列中取出消息并进行处理,如下所示:

while (NetworkingClient.DataMessages.Count > 0)
{
    DataMessage message = NetworkingClient.DataMessages.Dequeue();
    switch (message.messageType)
    {
       ...
    }
}

然而,每隔一段时间,我就会在switch (message.messageType)行收到一个NullReferenceException,并且我可以在调试器中看到该消息为null。

不可能在队列中放入null值(请参阅代码的第一位),这是使用队列的仅有的两件事。

队列不是线程安全的吗?可能是我在另一个线程排队的确切时刻退出了队列,这导致了故障吗?

多线程.NET队列问题

队列不是线程安全的,可能吗我正在排队另一个线程正在排队,并且这导致了故障?

没错。Queue不是线程安全的。线程安全队列是System.Collections.Concurrent.ConcurrentQueue。用它来解决你的问题。

    while (NetworkingClient.DataMessages.Count > 0)
    {
        // once every two weeks a context switch happens to be here.
        DataMessage message = NetworkingClient.DataMessages.Dequeue();
        switch (message.messageType)
        {
           ...
        }
    }

当您在该位置获得上下文切换时,第一个表达式的结果(NetworkingClient.DataMessages.Count > 0)对于两个线程都为true,并且对Dequeue()操作执行get的线程首先获取对象,第二个线程获取null(而不是InvalidOperationException,因为队列的内部状态没有完全更新以引发正确的异常)。

现在您有两个选项:

  1. 使用.NET 4.0并发队列

  2. 重构代码:

并使其看起来像这样:

while(true)
{
  DataMessage message = null;
  lock(NetworkingClient.DataMessages.SyncRoot) {
       if(NetworkingClient.DataMessages.Count > 0) {
          message = NetworkingClient.DataMessages.Dequeue();
       } else {
         break;
       }
    }
    // .. rest of your code
}

编辑:更新以反映赫德尔的评论。

如果您对确切的原因感兴趣:

Enqueue看起来像这样:

this._array[this._tail] = item;
this._tail = (this._tail + 1) % this._array.Length;
this._size++;
this._version++;

Dequeue是这样的:

T result = this._array[this._head];
this._array[this._head] = default(T);
this._head = (this._head + 1) % this._array.Length;
this._size--;
this._version++;

比赛是这样的:

  • 队列中有1个元素(head==tail),因此您的读取器线程开始出列,但在Dequeue中的第一行之后被中断
  • 然后将另一个元素入队并放置在位置CCD_ 9,该位置此时等于CCD_
  • 现在Dequeue恢复并用default(T)覆盖Enqueue刚刚插入的元素
  • 下次调用dequeue时,会得到默认值(T)(在您的情况下为null),而不是实际值