生产者消费者在c#中使用AutoReset事件

时间:2013-05-31 11:51:07

标签: c# multithreading

以下代码无法使用自动重置事件,我在做什么错误?

using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;

    namespace Threaddd
    {
        class Program
        {
            static int num = 0;
            static EventWaitHandle e = new AutoResetEvent(false);
            static object o = new object();

            static void Main(string[] args)
            {
                new Thread(Consumer).Start();
                new Thread(Producer).Start();

            }


            static void Producer()
            {
                while (true)
                {
                    if (num == 0)
                    {
                        num++;
                        Console.WriteLine("Produced " + num);
                        Thread.Sleep(1000);
                        e.Set();
                        e.WaitOne();

                    }
                }
            }

            static void Consumer()
            {
                while (true)
                {
                    if (num == 1)
                    {
                        Console.WriteLine("Consumed " + num);
                        Thread.Sleep(1000);
                        num--;
                        e.Set();
                        e.WaitOne();

                    }
                    else
                    {
                        e.WaitOne();
                    }
                }
            }
}

4 个答案:

答案 0 :(得分:2)

这不是真正的消费者/生产者模式实施 e.Set()将仅使用e.WaitOne()

发布正在等待的一个帖子

所以,当你写:

e.Set();
e.WaitOne();

在生产者线程上,您实际上没有启用使用者线程来获取信号

尝试以下方法:

        static void Producer()
        {
            while (true)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Produced " + num++);
                e.Set();
            }
        }

        static void Consumer()
        {
            while (true)
            {
                e.WaitOne();
                Console.WriteLine("Consumed " + num);
            }
        }

答案 1 :(得分:2)

看起来当Producer线程调用e.Set()时,它不会立即通知Consumer线程,因此Producer线程在调用e.WaitOne()时会消耗该事件。

来自http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx

“无法保证每次调用Set方法都会释放一个线程。如果两个调用太靠近,那么第二次调用就会在线程释放之前发生,只释放一个线程。如果第二次调用没有发生。另外,如果在没有线程等待并且AutoResetEvent已经发出信号的情况下调用了Set,则该调用无效。“

一个想法是为每个线程使用一个单独的事件,如提供的链接所示。

答案 2 :(得分:1)

如果您的消费者和生产者线程可以正常运行,您可以通过删除一些集合和等待来简化您的程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Threaddd
{
internal class Program
{
    private static int num = 0;
    private static EventWaitHandle e = new AutoResetEvent(false);
    private static object o = new object();

    private static void Main(string[] args)
    {
        new Thread(Consumer).Start();
        new Thread(Producer).Start();

    }


    private static void Producer()
    {
        while (true)
        {
            if (num == 0)
            {
                num++;
                Console.WriteLine("Produced " + num);
                Thread.Sleep(1000);
                e.Set();
            }
        }
    }

    private static void Consumer()
    {
        while (true)
        {
            if (num == 1)
            {
                Console.WriteLine("Consumed " + num);
                Thread.Sleep(1000);
                num--;
                e.WaitOne();
            }
        }
    }
}
}

如果这不是一个选项,那么您的生产者和消费者都必须拥有自己的事件。

答案 3 :(得分:1)

要将num保持在0和1之间,您可以使用以下模式并丢失if语句:

   class Program
   {
      static volatile int num = 0;

      // Initialized set to ensure that the producer goes first.
      static EventWaitHandle consumed = new AutoResetEvent(true);

      // Initialized not set to ensure consumer waits until first producer run.
      static EventWaitHandle produced = new AutoResetEvent(false);

      static void Main(string[] args)
      {
         new Thread(Consumer).Start();
         new Thread(Producer).Start();
      }

      static void Producer()
      {
         while (true)
         {
            consumed.WaitOne();
            num++;
            Console.WriteLine("Produced " + num);
            Thread.Sleep(1000);
            produced.Set();               
         }
      }

      static void Consumer()
      {
         while (true)
         {
            produced.WaitOne();
            Console.WriteLine("Consumed " + num);
            Thread.Sleep(1000);
            num--;
            consumed.Set();               
         }
      }
   }

值得指出的是,通常在生产者和消费者之间存在某种队列,以便生产者可以在每次消费者运行之间创建多个项目。我编写上述内容的方式将消费者和生产者放在不同的线程上是没有意义的,因为它们无法同时运行。