如何为吸烟者的情况实现监视器和系统线程



我正在处理的问题涉及使用功能性 Lock 或监视器结构,以仅向单独线程上的一个成员提供独占访问权限。下面是我对监视器的类定义(请注意,它与 c# 库给出的实际监视器类不同)。我正在尝试做的是使图片框在我的表单上出现或消失。

我试图添加表单的实例,以便可以访问各个图片框,但是,我的程序似乎冻结了。

namespace SmokersProblem
{
class monitor
{
    Form1 form = new Form1();
    Random num = new Random();
    object obj = new object();
    public monitor()
    {
    }
    public void agent(){
        form.pictureBox4.Visible = false;
        int r = num.Next(1, 4);
        if (r == 1)
        {
            // put lighter and paper on table
            smoker1();
        }
        else if (r == 2)
        {
            // put lighter and tobacco on table
            smoker2();
        }
        else
        {
            // put paper and tobacco on table 
            smoker3();
        }
    }
    public void smoker1()
    {
        //lock (obj)
        //{
            form.pictureBox9.Visible = true;
            form.pictureBox1.Visible = false;
            System.Threading.Thread.Sleep(5000);
            //agent();
       // }
    }
    public void smoker2()
    {
        //lock (obj)
        //{
            form.pictureBox10.Visible = true;
            form.pictureBox3.Visible = false;
            System.Threading.Thread.Sleep(5000);
            //agent();
        //}
    }
    public void smoker3()
    {
        //lock (obj)
        //{
            form.pictureBox11.Visible = true; 
            form.pictureBox2.Visible = false;
            System.Threading.Thread.Sleep(5000);
            //agent();
       // }
    }
}
 }

下面是我的表单代码,正如你在这里看到的,我尝试创建三个单独的线程,每个吸烟者一个。

namespace SmokersProblem
{
public  partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        Random rnd = new Random();
        int num = rnd.Next(1, 4);
        Object obj = new Object();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
    }
    private void button1_Click(object sender, EventArgs e)
    {
        pictureBox1.Visible = true;
        pictureBox2.Visible = true;
        pictureBox3.Visible = true;
        pictureBox8.Visible = false;
        pictureBox7.Visible = false;
        pictureBox6.Visible = false;
        monitor one = new monitor();
        one.agent();
        Thread vone = new Thread(one.smoker1);
        Thread two = new Thread(one.smoker2);
        Thread three = new Thread(one.smoker3);
        vone.Start();
        two.Start();
        three.Start();
    }
  }

}

实现这一点后,我去寻找看起来 OP 正在尝试实现的吸烟线程问题。此代码应该很容易适应该问题。

UI 冻结的原因是你在调用one.agent()时没有将其放入新线程中。 one.agent()休眠,这会阻止 UI 处理事件。

好的,我已经实现了一些代码来解决标签的吸烟者问题。显然,它可以改进,例如,通过不将表单耦合到线程。

我放了两个不同的锁定机制,并留下了一个注释掉。

从本质上讲,有三个标签可以是"吸烟"或"不吸烟"。主 UI 线程创建三个线程:

  1. 吸烟者1
  2. 吸烟者2
  3. 吸烟者3

每个线程不断尝试在 while 循环中获取锁定。当他们拿起锁时,他们将标签设置为"吸烟",等待几秒钟,然后将标签设置为"不吸烟"。这使用此答案中的线程安全代码。

public partial class Form1 : Form
{
    private bool running = false;
    public Label OneLabel { get; set; }
    public Label TwoLabel { get; set; }
    public Label ThreeLabel { get; set; }
    private MyMonitor one;
    private Thread vone;
    private Thread two;
    private Thread three; 
    public Form1()
    {
        InitializeComponent();
        OneLabel = new Label();
        OneLabel.Text = "Not Smoking";
        OneLabel.Location = new Point(10, 50);
        OneLabel.AutoSize = true;
        this.Controls.Add(OneLabel);
        TwoLabel = new Label();
        TwoLabel.Text = "Not Smoking";
        TwoLabel.Location = new Point(150, 50);
        this.Controls.Add(TwoLabel);
        ThreeLabel = new Label();
        ThreeLabel.Text = "Not Smoking";
        ThreeLabel.Location = new Point(300, 50);
        this.Controls.Add(ThreeLabel);
    }
    private void MainButton_Click(object sender, EventArgs e)
    {
        if (!running)
        {
            vone.Start();
            two.Start();
            three.Start();
            MainButton.Text = "Stop";
            running = true;
        }
        else
        {
            one.RequestStop();
            MainButton.Text = "Run";
            running = false;
        }
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        one = new MyMonitor(this);
        vone = new Thread(one.Smoker1);
        two = new Thread(one.Smoker2);
        three = new Thread(one.Smoker3);
    }
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (running)
        {
            one.RequestStop();
            running = false;
        }
    }
}
class MyMonitor
{
    private int x = 1;
    private Object obj = new Object();
    private Form1 _form;
    bool _finished = false;
    public MyMonitor(Form1 form)
    {
        _form = form;
    }
    public void Smoker1()
    {
        while (!_finished)
        {
            //lock (obj)
            //{
            //    _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
            //    System.Threading.Thread.Sleep(2000);
            //    _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
            //}
            try
            {
                Monitor.Enter(obj);
                try
                {
                    _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Smoking");
                    System.Threading.Thread.Sleep(2000);
                    _form.OneLabel.SetPropertyThreadSafe(() => _form.OneLabel.Text, "Not Smoking");
                }
                finally
                {
                    Monitor.Exit(obj);
                }
            }
            catch (SynchronizationLockException SyncEx)
            {
                Console.WriteLine(SyncEx.Message);
            }
        }
    }
    public void Smoker2()
    {
        while (!_finished)
        {
            //lock (obj)
            //{
            //    _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
            //    System.Threading.Thread.Sleep(2000);
            //    _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
            //}
            try
            {
                Monitor.Enter(obj);
                try
                {
                    _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Smoking");
                    System.Threading.Thread.Sleep(2000);
                    _form.TwoLabel.SetPropertyThreadSafe(() => _form.TwoLabel.Text, "Not Smoking");
                }
                finally
                {
                    Monitor.Exit(obj);
                }
            }
            catch (SynchronizationLockException SyncEx)
            {
                Console.WriteLine(SyncEx.Message);
            }
        }
    }
    public void Smoker3()
    {
        while (!_finished)
        {
            //lock (obj)
            //{
            //    _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
            //    System.Threading.Thread.Sleep(2000);
            //    _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
            //}
            try
            {
                Monitor.Enter(obj);
                try
                {
                    _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Smoking");
                    System.Threading.Thread.Sleep(2000);
                    _form.ThreeLabel.SetPropertyThreadSafe(() => _form.ThreeLabel.Text, "Not Smoking");
                }
                finally
                {
                    Monitor.Exit(obj);
                }
            }
            catch (SynchronizationLockException SyncEx)
            {
                Console.WriteLine(SyncEx.Message);
            }
        }
    }
    public void RequestStop()
    {
        _finished = true;
    }
}
//Thread Safe Extension Method
public static class Extensions
{
    private delegate void SetPropertyThreadSafeDelegate<TResult>(Control @this, Expression<Func<TResult>> property, TResult value);
    public static void SetPropertyThreadSafe<TResult>(this Control @this, Expression<Func<TResult>> property, TResult value)
    {
        var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
        if (propertyInfo == null ||
            !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
            @this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null)
        {
            throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
        }
        if (@this.InvokeRequired)
        {
            @this.Invoke(new SetPropertyThreadSafeDelegate<TResult>(SetPropertyThreadSafe), new object[] { @this, property, value });
        }
        else
        {
            @this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value });
        }
    }
}

我的程序似乎冻结了

我的 one.agent() 是允许其中一个吸烟者的代码部分 被召唤,所以他们可以吸烟。我为什么不想把它放在主要 法典?

因为您不应该从主 UI 线程使用 Sleep(),当您从按钮单击事件调用 one.agent() 时,就会发生这种情况。 当命中 Sleep(5000) 时,您告诉表单主 UI 线程在五秒钟内不执行任何操作,因此您会看到冻结。

要解决此问题,您需要 agent() 在单独的线程中执行 smoker1()、smoker2() 或 smoker3(),就像你在下面所做的那样。

但是,代码还有其他几个问题,在"修复"代码之前也必须解决这些问题......

下一个问题在于你在 monitor() 类中创建一个新的 Form1() 实例。 此 Form1() 实例与屏幕上可见的实例不同。 作用于它正在修改一种甚至从未被展示过的无形形式。 要对屏幕上实际可见的表单进行操作,需要您 (a) 在创建 monitor() 类时将对它的引用传递到 monitor() 类中,或者 (b) 让您的 monitor() 类在创建 monitor() 时再次引发 Form1() 订阅的自定义事件。

最后一个问题在于您尝试从主 UI 线程以外的线程中更改 UI 控件。 这将导致跨线程异常(除非您已关闭此功能,否则不应关闭此功能)。 有多种方法可以解决此问题,其中最基本的方法涉及使用委托和要尝试更新到的窗体/控件的 Invoke() 方法。

最新更新