2013-05-31 44 views
1

下面的代码不能使用自动重置事件,我在做什么错误?生产者消费者使用c#中的AutoReset事件#

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

如果您能告诉我们您期望代码执行的内容,它总是有帮助的。 –

+0

作为一个旁注,如果您使用的是.NET 4,那么会有使数据结构更加简单的数据结构,其中一个是BlockingCollection http://msdn.microsoft.com/zh-cn/library/dd267312.aspx – PeskyGnat

+0

当Semaphore可用时使用AutoResetEvent。 –

回答

2

看起来像生产者线程调用e.Set(),它不会立即通知Consumer线程,因此Producer线程在调用e.WaitOne()时会使用该事件。

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

“没有保证的设置方法每次调用会释放一个线程。如果两个呼叫靠得太近,让一个线程被释放之前发生的第二个电话,只有一个线程被释放,就好像第二次调用没有发生一样,而且,如果在没有线程在等待的情况下调用Set,并且AutoResetEvent已经发出信号,则调用不起作用。

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

+1

所以这意味着,如果生产线程调用e.set(); e.waithandle(); e。set()可能不会被用来立即通知消费者线程,但是如果我已经调用了e.waithandle(),它可能会被生产者线程使用。有趣的......... Thnks – kamal

+0

正确,一个简单的测试看到将只有e.WaitOne()在消费者和e.Set(); e.WaitOne();在生产者中,经过几次运行后,有时生产者会设置并在消费者使用之前在WaitOne中使用该事件 – PeskyGnat

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); 
      } 
     } 
+0

这会导致num在某些情况下会大于1。如果消费者线程暂停超过1秒钟并且在e已经设置时被调用,则会发生这种情况。 –

+0

这是正确的,我认为@kamal添加了num变量只是为了调试。你对@kamal有什么要说的? – Liel

+0

是的,你是正确的 – kamal

1

如果你的好你的消费者和生产者线程运行野生您可以通过删除一些集和waitones的简化程序:

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

如果这是不是一种选择,既您的生产和消费(S)必须有自己的事件。

+0

当num> 1时,消费者会发生什么情况?注意if(num == 1)。 –

+0

@MartinBrown只有一个生产者线程,它只在它等于0时递增num。所以我不确定它会如何大于1。 –

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

这是值得指出的是,通常有某种生产者和消费者等等之间的队列生产者可以在消费者的每次运行之间创建多个项目。我已经写过上面的这种方法,因为消费者和生产者在单独的线程上是没有意义的,因为它们不能同时运行。