如何实现对吸烟者情况的监测和系统线程

时间:2013-10-22 00:19:19

标签: c# multithreading winforms

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

    }

  }

}

2 个答案:

答案 0 :(得分: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()方法。

答案 1 :(得分:0)

执行此操作后,我去寻找OP正在尝试实施的Smoker Thread Problem。此代码应该很容易适应该问题。

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

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

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

基本上,有三个标签可以是“吸烟”或“不吸烟”。主UI线程创建三个线程:

  1. Smoker1
  2. Smoker2
  3. Smoker3
  4. 每个线程不断尝试在while循环中获取锁定。当他们拿锁时,他们将标签设置为“吸烟”,等待几秒钟,然后将其标签设置为“不吸烟”。这使用来自this answer的线程安全代码。

    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 });
            }
        }
    }