队列.Dequeue 返回 null

发布于 2024-08-21 11:36:05 字数 370 浏览 7 评论 0原文

我有一个场景,其中

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

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

-

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

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

I have a scenario where

  • multiple threads are pushing data on a Queue

  • only ONE thread is processing data using code below

code -

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

But sometimes, queue.Dequeue() returns null in the scenario above
What gives ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

拥抱影子 2024-08-28 11:36:05

您需要阅读此内容博客文章

另外,这是用于线程之间通信的“通道”的一个非常小的框架:

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();
        }
    }
}

You need to read this blog post.

Also, here's a very minimal skeleton of a "channel" for communication between threads:

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();
        }
    }
}
季末如歌 2024-08-28 11:36:05

您需要同步对队列的访问。将 lock 语句放在访问队列(读取和写入)的所有代码部分周围。如果您从多个线程同时访问队列,则内部结构可能会被损坏,并且几乎任何事情都可能发生。

You need to synchronise the access to the queue. Put lock statements around all code sections that access the queue (both reading and writing). If you access the queue simultaneously from multiple threads the internal structures may be corrupted and just about anything can happen.

度的依靠╰つ 2024-08-28 11:36:05

你说:

多个线程正在将数据推送到队列

Queue.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.Enqueue 这样的非线程安全方法是如何工作的当访问不同步时可能会变得混乱。它没有具体解释你的问题中发生了什么;我的目的只是指出您所做的事情不是线程安全的,并且会导致意外行为

You say:

multiple threads are pushing data on a Queue

The Queue<T>.Enqueue method is not thread-safe. This means that work gets done within the Enqueue method that needs to be synchronized if multiple threads are calling it. A simple example would be updating the Count property. It's a safe bet that somewhere in the Enqueue method there's a line that looks something like this:

++count;

But as we all know, this isn't an atomic operation. It's really more like this (in terms of what's actually happening):

int newCount = count + 1;
count = newCount;

So say the count is currently 5, and Thread 1 gets past int newCount = count + 1... then Thread 1 thinks, "OK, the count is now 5, so I'll make it 6." But the very next operation that gets executed is where Thread 2 gets to int newCount = count + 1 and thinks the same thing as Thread 1 ("the count is now 6"). So two items have just been added to the queue, but the count only went from 5 to 6.

This is just a very basic example of how a non thread-safe method like Queue<T>.Enqueue can get messed up when access is not synchronized. It doesn't specifically explain what's happening in your question; my intention is simply to point out that what you're doing is not thread-safe and will cause unexpected behavior.

萌梦深 2024-08-28 11:36:05

Guffa 是正确的,有多个线程读取和写入队列会导致问题,因为 Queue不是线程安全的。

如果您使用的是 .NET 4,请使用 ConcurrentQueue类,它是线程安全的。如果您不是使用 .NET 3 或更早版本,您可以自己进行锁定(正如 Guffa 指出的那样),也可以使用第 3 方库。

Guffa is correct, having multiple threads reading and writing to the queue will cause problems, because Queue<T> is not thread safe.

If you're on .NET 4, use the ConcurrentQueue<T> class, which is thread safe. If you're not on .NET 3 or earlier, you can either do your own locking, as Guffa pointed out, or use a 3rd party library.

垂暮老矣 2024-08-28 11:36:05

确保没有任何东西将 null 值推入队列。允许 null 作为排队值。另外,根据此文档,只有Queue; 的静态成员是线程安全的,所以要小心跨线程的读写。

Make sure nothing's pushing null values into the queue. nulls are allowed as queued values. Also, according to this document, only Queue<T>'s static members are thread-safe, so beware of doing reading and writing across threads.

痞味浪人 2024-08-28 11:36:05

为什么不这样解决问题呢?

while (IsRunning)
{
    do
    {
        MyObj myObj = queue.Dequeue();
        if (myObj != null)
        {
            DoSomethingWith(myObj);
        }
    } while (myObj != null);

    myAutoResetEvent.WaitOne();
}

更新

好吧,在阅读了 Earwickers 的评论和所有其他答案后,你是对的,我只是错了。因此请不要在多线程上下文中使用上面的代码

Why don't you solve the problem that way?

while (IsRunning)
{
    do
    {
        MyObj myObj = queue.Dequeue();
        if (myObj != null)
        {
            DoSomethingWith(myObj);
        }
    } while (myObj != null);

    myAutoResetEvent.WaitOne();
}

Update

Ok, after reading Earwickers comment and all the other answers, you are all right and i'm just false. So please don't use the code above in multithreaded contexts .

风轻花落早 2024-08-28 11:36:05

如果您碰巧使用非通用队列(不是我建议使用它),您可以使用 Queue.Synchronized 方法来获取线程安全包装器:

Queue queue = Queue.Synchronized(new Queue());

否则您应该像其他人建议的那样自行锁定。

If you happen to be using non-generic Queue (not that I advise to use it), you can use Queue.Synchronized method to get a thread-safe wrapper:

Queue queue = Queue.Synchronized(new Queue());

Otherwise you should take care of locking yourself, as others suggest.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文