1

我正在处理的问题是使用功能锁或监视器结构来提供对单独线程上的一个成员的独占访问。下面是我对监视器的类定义(请注意,它与 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();

    }

  }

}

4

2 回答 2

0

在实现了这个之后,我去寻找看起来 OP 正在尝试实现的Smoker Thread 问题。这段代码应该很容易适应这个问题。

您的 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 });
        }
    }
}
于 2013-10-22T03:27:46.693 回答
0

我的程序似乎冻结了

我的 one.agent() 是允许调用其中一名吸烟者的代码部分,因此他们可以吸烟。为什么我不想在主代码中使用它?

因为您不应该在主 UI 线程中使用 Sleep(),当您从 Button Click 事件调用 one.agent() 时会发生这种情况。当 Sleep(5000) 被击中时,您是在告诉表单主 UI 线程在 5 秒钟内不要做任何事情,因此您会看到冻结。

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

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

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

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

于 2013-10-22T01:32:07.113 回答