2013-10-20 47 views
3

我在Cigarette Smoker Problem工作。多線程與監視器進入/退出,等待/脈衝,鎖

我只能使用Monitor類。沒有信號/信號量。 (是的,這是針對學校的,但不是家庭作業,我的實際考試只是一個免費練習,而且我真的需要做好準備,所以讓這個程序起作用會幫助我很多)。

我的問題是,我不知道哪些對象「鎖定」等等。(你可以看到我已經與這一點,只是隨機的對象很多試過)

我有4個線程,1經銷商,3吸菸者。我有一個經銷商類和一個吸菸者班。目前,我所有的吸菸者都去Monitor.Wait(),並且有1到2個ingridients,然後他們再也不會離開它,即使我在每次經銷商將ingridients放到桌子上時都打電話給Monitor.PulseAll()。我認爲這是因爲我使用了錯誤的對象作爲參數,我完全不知道。

textbox和字符串主要用於在我的WPF類中提供數據。

吸菸者類別:請原諒德語變量名稱。 (塔巴克=菸草,混凝=紙,streichhölzer= firethingies,zutat = ingridients,rauchzeit = smoketime)

class Raucher 
{ 
    enum Zutaten { Tabak, Papier, Streichhölzer, Leer }; 

    public static Random r = new Random(); 
    int id; 
    String status = ""; 
    public static int rauchzeit, rauchzeitvar, drehzeit, drehzeitvar; 
    TextBox txtbox; 
    Zutaten zutat1; 
    Zutaten zutat2; 
    Zutaten zutat3; 
    public static Dealer dealer; 


    public Raucher(Int32 id, Int32 rauchzeit, Int32 rauchzeitvar, Int32 drehzeit, Int32 drehzeitvar, TextBox status1, TextBox status2, TextBox status3, Dealer dealer) 
    { 
     this.id = id; 
     Raucher.rauchzeit = rauchzeit; 
     Raucher.rauchzeitvar = rauchzeitvar; 
     Raucher.drehzeit = drehzeit; 
     Raucher.drehzeitvar = drehzeitvar; 
     Raucher.dealer = dealer; 
     status = "Warten"; 
     switch (id) 
     { 
      case (0): 
       txtbox = status1; 
       zutat1 = Zutaten.Tabak; 
       break; 
      case (1): 
       txtbox = status2; 
       zutat1 = Zutaten.Papier; 
       break; 
      case (2): 
       txtbox = status3; 
       zutat1 = Zutaten.Streichhölzer; 
       break; 
     } 
     zutat2 = Zutaten.Leer; 
     zutat3 = Zutaten.Leer; 
    } 

    public void updateText() 
    { 
     try 
     { 
      txtbox.Dispatcher.BeginInvoke(
       System.Windows.Threading.DispatcherPriority.Normal 
       , new System.Windows.Threading.DispatcherOperationCallback(delegate 
       { 
        txtbox.Text = status; 
        switch (status) 
        { 
         case "Drehen": 
          txtbox.Background = Brushes.White; 
          break; 
         case "Rauchen": 
          txtbox.Background = Brushes.Green; 
          break; 
         case "Warten": 
          txtbox.Background = Brushes.Red; 
          break; 
        } 

        txtbox.UpdateLayout(); 
        return null; 
       }), null); 
     } 
     catch (Exception ex) 
     { 
      System.Diagnostics.Debug.WriteLine(ex.ToString()); 
     } 
    } 

    public static readonly object _locker = new object(); 

    public void Go() 
    { 
     while (true) 
     { 
      lock (_locker) 
      { 
       Console.WriteLine("Tabak: " + dealer.tabak); 
       Console.WriteLine("Papier: " + dealer.papier); 
       Console.WriteLine("Streichhölzer: " + dealer.streichhölzer); 
       if (!dealer.tabak && !dealer.papier && !dealer.streichhölzer) 
       { 
         Monitor.PulseAll(_locker);     
       } 
       if (zutat1 == Zutaten.Tabak) 
       { 
        if (dealer.papier && zutat2 == Zutaten.Leer) 
        { 
         dealer.takePapier(); 
         zutat2 = Zutaten.Papier; 
        } 
        if (dealer.streichhölzer && zutat3 == Zutaten.Leer) 
        { 
         dealer.takeStreichhölzer(); 
         zutat3 = Zutaten.Streichhölzer; 
        } 
        if (zutat2 == Zutaten.Papier && zutat3 == Zutaten.Streichhölzer) 
        { 
         status = "Drehen"; 
         updateText(); 
         Thread.Sleep(r.Next(drehzeit - drehzeitvar, drehzeit + drehzeitvar)); 
         status = "Rauchen"; 
         updateText(); 
         Thread.Sleep(r.Next(rauchzeit - rauchzeitvar, rauchzeit + rauchzeitvar)); 
         zutat2 = Zutaten.Leer; 
         zutat3 = Zutaten.Leer; 
        } 
        else 
        { 
         Monitor.Wait(_locker); 
        } 
       } 
       if (zutat1 == Zutaten.Papier) 
       { 
        if (dealer.tabak && zutat2 == Zutaten.Leer) 
        { 
         dealer.takeTabak(); 
         zutat2 = Zutaten.Tabak; 
        } 
        if (dealer.streichhölzer && zutat3 == Zutaten.Leer) 
        { 
         dealer.takeStreichhölzer(); 
         zutat3 = Zutaten.Streichhölzer; 
        } 
        if (zutat2 == Zutaten.Tabak && zutat3 == Zutaten.Streichhölzer) 
        { 
         status = "Drehen"; 
         updateText(); 
         Thread.Sleep(r.Next(drehzeit - drehzeitvar, drehzeit + drehzeitvar)); 
         status = "Rauchen"; 
         updateText(); 
         Thread.Sleep(r.Next(rauchzeit - rauchzeitvar, rauchzeit + rauchzeitvar)); 
         zutat2 = Zutaten.Leer; 
         zutat3 = Zutaten.Leer; 
        } 
        else 
        { 
         Monitor.Wait(_locker); 
        } 
       } 
       if (zutat1 == Zutaten.Streichhölzer) 
       { 
        if (dealer.papier && zutat2 == Zutaten.Leer) 
        { 
         dealer.takePapier(); 
         zutat2 = Zutaten.Papier; 
        } 
        if (dealer.tabak && zutat3 == Zutaten.Leer) 
        { 
         dealer.takeTabak(); 
         zutat3 = Zutaten.Tabak; 
        } 
        if (zutat2 == Zutaten.Papier && zutat3 == Zutaten.Tabak) 
        { 
         status = "Drehen"; 
         updateText(); 
         Thread.Sleep(r.Next(drehzeit - drehzeitvar, drehzeit + drehzeitvar)); 
         status = "Rauchen"; 
         updateText(); 
         Thread.Sleep(r.Next(rauchzeit - rauchzeitvar, rauchzeit + rauchzeitvar)); 
         zutat2 = Zutaten.Leer; 
         zutat3 = Zutaten.Leer; 
        } 
        else 
        { 
         Monitor.Wait(_locker); 
        } 
       } 
      } 
     } 
    } 
} 

經銷商類別:

class Dealer 
{ 
    public static Random r = new Random(); 
    public Boolean tabak = false; 
    public Boolean papier = false; 
    public Boolean streichhölzer = false; 
    public String zutaten; 

    public Boolean isEmpty() 
    { 
     return !(tabak || papier || streichhölzer); 
    } 

    public void setTabak() 
    { 
     tabak = true; 
    } 

    public void setPapier() 
    { 
     papier = true; 
    } 

    public void setStreichhölzer() 
    { 
     streichhölzer = true; 
    } 

    public void takeTabak() 
    { 
     tabak = false; 
    } 

    public void takePapier() 
    { 
     papier = false; 
    } 

    public void takeStreichhölzer() 
    { 
     streichhölzer = false; 
    } 

    TextBox status; 

    public Dealer(TextBox status) 
    { 
     this.status = status; 
    } 

    public static readonly object _locker = new object(); 

    public void Go() 
    { 
     while (true) 
     { 
      if (isEmpty()) 
      { 
       lock (this) 
       { 
        if (!tabak && !papier && !streichhölzer) 
        { 
         int zahl1 = r.Next(0, 3); 
         int zahl2 = r.Next(0, 3); 
         while (zahl1 == zahl2) 
         { 
          zahl2 = r.Next(0, 3); 
         } 
         switch (zahl1) 
         { 
          case (0): 
           setTabak(); 
           break; 
          case (1): 
           setPapier(); 
           break; 
          case (2): 
           setStreichhölzer(); 
           break; 
         } 
         switch (zahl2) 
         { 
          case (0): 
           setTabak(); 
           break; 
          case (1): 
           setPapier(); 
           break; 
          case (2): 
           setStreichhölzer(); 
           break; 
         } 
         updateText(); 
         Monitor.PulseAll(this); 
        } 
       } 
      } 
     } 
    } 

    public void updateText() 
    { 
     try 
     { 
      status.Dispatcher.BeginInvoke(
       System.Windows.Threading.DispatcherPriority.Normal 
       , new System.Windows.Threading.DispatcherOperationCallback(delegate 
       { 
        zutaten = ""; 
        if (tabak) 
        { 
         zutaten += " Tabak "; 
        } 
        if (papier) 
        { 
         zutaten += " Papier "; 
        } 
        if (streichhölzer) 
        { 
         zutaten += " Streichhölzer "; 
        } 
        status.Text = zutaten; 
        status.UpdateLayout(); 
        return null; 
       }), null); 
     } 
     catch (Exception ex) 
     { 
      System.Diagnostics.Debug.WriteLine(ex.ToString()); 
     } 
    } 
} 
+0

它實際上是用方法名稱與德語變元編譯,如「setStreichhölzer」嗎? – elgonzo

+0

@elgonzo雖然我已經找到非ASCII字符的標識符不適合不同的原因。不過,C#具有Unicode標識符,而.cs文件默認爲UTF8。 – usr

回答

2

有幾件事情錯:

  1. 吸菸者和經銷商類都有自己的_locker對象。吸菸者和經銷商線程之間的同步不會發生,如果他們不以某種方式訪問​​相同的同步原語。

  2. 在經銷商,你鎖定「這個」,並在它脈搏。這是沒有意義的,因爲沒有其他線程會知道它,因爲只有一個線程(經銷商)使用它。

  3. 鎖定「this」或public properties/fields是不好的做法。在較大的軟件項目中,這種做法很容易導致死鎖。

  4. 不要讓每個類使用彼此的同步對象。讓經銷商有一個同步對象,吸菸者使用它來同步對經銷商資源的訪問。但是,讓多個類混淆同步對象實際上也是一種非常糟糕的做法。

  5. 更好的是在Dealer類中有一個私有同步對象,並且以這種方式實現您的應用程序邏輯,以便同步可以在Dealer類中內部處理,對任何吸菸者完全透明。

+0

非常感謝您的回覆。我仍然在學習,現在我明白瞭如何鎖定對象的作品。因爲我的時間,當我找到更多的時間時,我會按照你在5中所描述的那樣去嘗試。現在就讓我們知道它是否工作,事先已經預先:) –