带监视器的多线程进入/退出,等待/脉冲,锁定

时间:2013-10-20 17:10:18

标签: c# multithreading monitor

我正在Cigarette Smoker Problem工作。

我只应该使用Monitor类。没有信号/信号量。 (是的,这是针对学校的,但不是家庭作业,只是我的实际测试的免费练习,我真的需要做好准备,所以让这个程序工作对我有很多帮助。)

我的问题是我不知道哪些物体要“锁定”等等(正如你所看到的,我已经尝试了很多这个和随机物品)

我有4个主题,1个经销商,3个吸烟者。我有经销商类和吸烟者类。目前,我所有的吸烟者都去了Monitor.Wait()并且喜欢1或2个ingridients,然后他们再也没有离开它,即使每次经销商将ingridients放在桌子上时我都会调用Monitor.PulseAll()。我认为这是因为我使用错误的对象作为参数,我完全不知道。

文本框和字符串主要用于在我的WPF类中提供数据。

吸烟者类:请原谅德语变量名。 (tabak = tabacco,papier = paper,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());
        }
    }
}

1 个答案:

答案 0 :(得分:2)

有几件事是错的:

  1. Smoker和Dealer类都有自己的_locker对象。如果他们不以某种方式访问​​相同的同步原语,则不会发生Smoker和Dealer线程之间的同步。

  2. 在经销商中,您锁定“this”,并在其上发出脉冲。这是没有意义的,因为没有其他线程会知道它,因为只有一个线程(经销商)使用它。

  3. 锁定“此”或公共属性/字段是不好的做法。在较大的软件项目中,这种做法很容易导致死锁情况。

  4. 不要让每个类使用彼此的同步对象。只需让经销商有一个同步对象,吸烟者使用该同步对象来同步对经销商资源的访问。但是让多个类乱用同步对象实际上也是一种非常糟糕的做法。

  5. 更好的方法是在Dealer类中拥有一个私有同步对象,并以这种方式实现应用程序逻辑,这样只能在Dealer类中内部处理同步,对任何Smoker完全透明。