1

下面的代码在使用自动重置事件时不起作用,我在做什么错?

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

4 回答 4

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);
            }
        }
于 2013-05-31T11:57:00.050 回答
2

貌似Producer线程在调用e.Set()的时候,并没有立即通知Consumer线程,所以Producer线程在调用e.WaitOne()的时候消费了这个事件。

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

“不能保证每次调用Set方法都会释放一个线程。如果两个调用靠得太近,以至于第二个调用发生在一个线程被释放之前,那么就只释放一个线程。就好像第二个调用没有发生。此外,如果在没有线程等待且 AutoResetEvent 已发出信号时调用 Set,则调用无效。

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

于 2013-05-31T12:03:16.310 回答
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();
            }
        }
    }
}
}

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

于 2013-05-31T12:09:44.327 回答
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();               
         }
      }
   }

值得指出的是,生产者和消费者之间通常存在某种队列,以便生产者可以在消费者的每次运行之间创建多个项目。我写上面的方式没有必要让消费者和生产者在不同的线程上,因为它们不能同时运行。

于 2013-05-31T12:39:49.780 回答