在C#中实现阻塞队列

use*_*458 13 c# queue multithreading

我使用下面的代码来实现和测试阻塞队列.我通过启动5个并发线程(删除程序)来测试队列,以便将项目从队列中拉出来,阻塞队列是否为空,以及1个并发线程(加法器)将项目间接添加到队列中.但是,如果我让它运行的时间足够长,我会得到一个异常,因为即使队列为空,其中一个卸载线程也会处于等待状态.

有谁知道为什么我得到例外?请注意,我很想知道为什么这不起作用而不是工作解决方案(就像我可以谷歌那样).

我非常感谢你的帮助.

using System;
using System.Threading;
using System.Collections.Generic;

namespace Code
{
    class Queue<T>
    {
        private List<T> q = new List<T>();

        public void Add(T item)
        {
            lock (q)
            {
                q.Add(item);
                if (q.Count == 1)
                {
                    Monitor.Pulse(q);
                }
            }
        }

        public T Remove()
        {
            lock (q)
            {
                if (q.Count == 0)
                {
                    Monitor.Wait(q);
                }
                T item = q[q.Count - 1];
                q.RemoveAt(q.Count - 1);
                return item;
            }
        }
    }

    class Program
    {
        static Random r = new Random();
        static Queue<int> q = new Queue<int>();
        static int count = 1;
        static void Adder()
        {
            while (true)
            {
                Thread.Sleep(1000 * ((r.Next() % 5) + 1));
                Console.WriteLine("Will try to add");
                q.Add(count++);
            }
        }

        static void Remover()
        {
            while (true)
            {
                Thread.Sleep(1000 * ((r.Next() % 5) + 1));
                Console.WriteLine("Will try to remove");
                int item = q.Remove();
                Console.WriteLine("Removed " + item);
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Test");

            for (int i = 0; i < 5; i++)
            {
                Thread remover = new Thread(Remover);
                remover.Start();
            }

            Thread adder = new Thread(Adder);
            adder.Start();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 17

如果我让它运行的时间足够长,我会得到一个例外,因为即使队列为空,其中一个卸载线程也会处于等待状态.有谁知道为什么我得到例外?

问题很奇怪,因为显然你知道答案:你的第一句话回答了第二句所提出的问题.您得到异常是因为当队列为空时,卸载线程退出等待状态.

要解决这个问题,你需要使用循环而不是"if".正确的代码是:

while(q.Count == 0) Monitor.Wait(q);
Run Code Online (Sandbox Code Playgroud)

if(q.Count == 0) Monitor.Wait(q);
Run Code Online (Sandbox Code Playgroud)

更新:

一位意见提供者指出,或许你的问题是"在什么情况下消费者线程可以在队列为空时获得监视器?"

嗯,你比我们更能回答这个问题,因为你是运行程序并查看输出的人.但就在我的脑海中,这是一种可能发生的方式:

  • 消费者线程1:等待
  • 消费者线程2:准备好了
  • 生产者线程3:拥有监视器
  • 队列中有一个元素.
  • 线程3脉冲.
  • 线程1进入就绪状态.
  • 线程3放弃了监视器.
  • 线程2进入监视器.
  • 线程2使用队列中的项目
  • 线程2放弃了监视器.
  • 线程1进入监视器.

现在,线程1在监视器中有一个空队列.

一般来说,在推理这些问题时,你应该把"脉冲"看作是一只带有附注的鸽子.一旦被释放,它与发送者没有任何联系,如果它找不到它的家,它就会在荒野中消失,其消息未被传递.你知道什么时候Pulse就是如果有任何线程在等待,那么一个线程将来会在某个时候进入就绪状态; 你不知道关于线程操作的相对时间的任何其他信息.

  • @GETah`Monitor.Wait()`并不意味着"等待这个锁",这意味着"我要产生显示器目前我自己的,直到另一个线程脉冲,让我知道,我可以继续下去." 实际上**需要*拥有您等待的对象的监视器. (4认同)
  • @Getah no,Monitor.Wait释放锁并将其置于等待状态,这在脉冲发生时发生 (2认同)