2011-09-19 55 views
3

我製作了這個生產者 - 消費者樣本,但我不知道它爲什麼會在最後凍結。 問題在哪裏?如果我在setNum(-99)行放置斷點,然後在休息之後繼續完成。 請同時告訴我,這段代碼是否正確並且線程安全。它必須像那樣工作,與此同時,消費者正在處理其給定的價值,生產者的所有其他價值都必須被忽略。 我對多線程很陌生。爲什麼這個consumer-producer線程被凍結?

class Program 
{ 
    delegate void SetNumberDelegate(int number); 
    static void Main(string[] args) 
    { 
     Random rnd = new Random(); 
     ConsumerClass consumerClass = new ConsumerClass(); 
     SetNumberDelegate setNum = new SetNumberDelegate(consumerClass.setNumber); 
     Thread.Sleep(20); 
     int num; 
     int count = 0; 
     Console.WriteLine("Start"); 
     while (count++ < 100) 
     { 
      num = rnd.Next(0, 100); 
      Console.WriteLine("Generated number {0}", num); 
      if (num > 30) 
      { 
       setNum(num); 
      } 
     } 
     setNum(-99); 
     Console.WriteLine("End"); 
     Console.ReadKey(); 
    } 
} 

class ConsumerClass : IDisposable 
{ 
    private int number; 
    private object locker = new object(); 
    private EventWaitHandle _wh = new AutoResetEvent(false); 
    private Thread _consumerThread; 

    public ConsumerClass() 
    { 
     number = -1; 
     _consumerThread = new Thread(consumeNumbers); 
     _consumerThread.Start(); 
    } 

    public void Dispose() 
    { 
     setNumber(-99); 
     _consumerThread.Join();   
     _wh.Close(); 
    } 

    public void setNumber(int num) 
    { 
     if (Monitor.TryEnter(locker)) 
     { 
      try 
      { 
       number = num; 
       Console.WriteLine("Setting number {0}", number); 
      } 
      finally 
      { 
       // Ensure that the lock is released. 
       Monitor.Exit(locker); 
      } 
      _wh.Set(); 
     } 
    } 

    public void consumeNumbers() 
    { 
     while (true) 
     { 
      Monitor.Enter(locker); 
      if (number > -1) 
      { 
       try 
       { 
        Console.WriteLine("Processing number:{0}", number); 
        // simulate some work with number e.g. computing and storing to db 
        Thread.Sleep(20); 
        Console.WriteLine("Done"); 
        number = -1; 
       } 
       finally 
       { 
        Monitor.Exit(locker); 
       } 
      } 
      else 
      { 
       if (number == -99) 
       { 
        Console.WriteLine("Consumer thread exit"); 
        return; 
       } 
       Monitor.Exit(locker); 
       _wh.WaitOne();   // No more tasks - wait for a signal 
      } 
     } 
    } 
} 
+0

CodeReview的問題應該提供工作片斷,尋找重構,模式使用等方面的提示。如果某些內容不起作用,則應將問題發佈到StackOverflow。 – IAbstract

+0

有權限的人請將其移至stackoverflow。謝謝。 – BreteP

+0

你可以標記並請管理員遷移... – IAbstract

回答

2

重寫setNumber這樣看到你的問題:

public void setNumber(int num) { 
    if (Monitor.TryEnter(locker)) { 
     // etc.. 
    } 
    else Console.WriteLine("Number {0} will never make it to the consumer", num); 
} 

你必須阻止,等待消費者準備好消費或使用隊列。

0

Monitor.TryEnter(locker);通常會失敗(包括-99),所以你不打算設置相當數量的價值觀,這就是爲什麼缺乏設置語句的輸出。這是因爲它不會等待獲取鎖定,它只會返回false。

0

問題出現在代碼的最後部分。你持有鎖,當你執行這個:

 else 
     { 
      if (number == -99) 
      { 
       Console.WriteLine("Consumer thread exit"); 
       return; 
      } 
      Monitor.Exit(locker); 
      _wh.WaitOne();   // No more tasks - wait for a signal 
     } 

所以,如果number == 99,不釋放鎖的方法返回。您的ConsumeNumbers方法過於複雜。你可以簡化它:

while (true) 
{ 
    _wh.WaitOne(); 
    lock (locker) 
    { 
     if (number == -99) 
      break; 
     if (number > -1) 
     { 
      // process the number. 
      number = -1; 
     } 
    } 
} 

這將做同樣的事情,並且是更簡單的代碼。

順便說一句,構建:

lock (locker) 
{ 
    // do stuff here 
} 

是一樣的:

Monitor.Enter(locker); 
try 
{ 
    // do stuff here 
} 
finally 
{ 
    Monitor.Exit(locker); 
}