代码之家  ›  专栏  ›  技术社区  ›  neouser99

C#线程和队列

  •  11
  • neouser99  · 技术社区  · 17 年前

    void Runner() {
        // member variable
        queue = Queue.Synchronized(new Queue());
        while (true) {
            if (0 < queue.Count) {
                queue.Dequeue();
            }
        }
    }
    

    这是在单个线程中运行的:

    var t = new Thread(Runner);
    t.IsBackground = true;
    t.Start();
    

    其他事件在其他地方“排队”。我所看到的是,在一段时间内,出列实际上会抛出InvalidOperationException,queue empty。这应该是不可能的,因为计数如何保证有东西在那里,我肯定没有其他东西是“出列”的。

    问题:

    1. 线程是否可能在Dequeue语句中以某种方式重新启动(过期、重置…),但在它已删除一个项之后立即重新启动?

    编辑(澄清):

    这些代码段是实现后台助手线程的包装器类的一部分。此处的出列是唯一的出列,所有的入/出列都在同步成员变量(queue)上。

    5 回复  |  直到 17 年前
        1
  •  11
  •   Erich Mirabal    17 年前

    使用Reflector,您可以看到,不,在添加项目之前,计数不会增加。

    正如本所指出的,看起来确实有很多人在叫“出列”。

    你说你肯定没有其他东西在叫出列。这是因为只有一个线程调用dequeue吗?是否在其他任何地方调用了“出列”?

    编辑:

    它运行了多长时间才出现错误?也许你可以分享更多的代码。

    class Program
    {
        static Queue q = Queue.Synchronized(new Queue());
        static bool running = true;
    
        static void Main()
        {
            Thread producer1 = new Thread(() =>
                {
                    while (running)
                    {
                        q.Enqueue(Guid.NewGuid());
                        Thread.Sleep(100);
                    }
                });
    
            Thread producer2 = new Thread(() =>
            {
                while (running)
                {
                    q.Enqueue(Guid.NewGuid());
                    Thread.Sleep(25);
                }
            });
    
            Thread consumer = new Thread(() =>
                {
                    while (running)
                    {
                        if (q.Count > 0)
                        {
                            Guid g = (Guid)q.Dequeue();
                            Console.Write(g.ToString() + " ");
                        }
                        else
                        {
                            Console.Write(" . ");
                        }
                        Thread.Sleep(1);
                    }
                });
            consumer.IsBackground = true;
    
            consumer.Start();
            producer1.Start();
            producer2.Start();
    
            Console.ReadLine();
    
            running = false;
        }
    }
    
        2
  •  3
  •   Ben S    17 年前

    1. (0 < queue.Count)
    2. 这条线会 preempted 另一个线程运行。
    3. 另一个线程从队列中删除一个项目,并清空它。
    4. 此线程继续执行,但现在位于if块内,并尝试将空列表出列。

    然而,你说没有其他东西是排队。。。

    尝试在if块内输出计数。如果您看到计数向下跳转,则其他人正在排队。

        3
  •  3
  •   Mike    17 年前

    这里有一个可能的答案 the MSDN page 关于这一主题:

    在集合中枚举是不正确的 程序即使是在收集 已同步,其他线程仍可以 修改集合,这将导致 要引发异常的枚举数。 在整个过程中收集 枚举或捕获异常 由其他公司所做的变更引起的 线程。

    我的猜测是,你是对的——在某个时刻,出现了一个竞争条件,你最终退出了一些不存在的东西。

    互斥锁或监视器。锁定可能适用于此。

        4
  •  2
  •   Ryan    16 年前

    “排队”数据的其他区域是否也使用相同的同步队列对象?为了使Queue.Synchronized具有线程安全性,所有入队和出队操作都必须使用相同的同步队列对象。

    从…起 MSDN :

    为了保证螺纹的安全性 只通过这个包装。

    编辑: 如果您对许多涉及重计算的项目进行循环,或者如果使用的是长期线程循环(通信等),则应考虑具有等待功能,例如 System.Threading.Thread.Sleep , System.Threading.WaitHandle.WaitOne , System.Threading.WaitHandle.WaitAll 或 System.Threading.WaitHandle.WaitAny 在循环中,否则可能会破坏系统性能。

        5
  •  1
  •   Community Mohan Dere    9 年前

    问题1:如果您使用的是同步队列,那么:不,您是安全的!但是您需要在供应商和进料器的两侧使用同步实例。

    问题2:当没有工作要做时终止工作线程是一项简单的工作。但是,无论哪种方式,您都需要一个监视线程,或者只要队列有事情要做,就让队列启动一个后台工作线程。最后一个听起来更像ActiveObject模式,而不是一个简单的队列(单一负责模式说它应该只进行排队)。

    另外,我会选择阻塞队列,而不是上面的代码。代码的工作方式需要CPU处理能力,即使没有工作要做。阻塞队列允许您的工作线程在无事可做时睡眠。您可以在不使用CPU处理能力的情况下运行多个休眠线程。

    C#没有提供阻塞队列实现,但是有很多。看到这个了吗 example 还有这个 one .