1

The problem that I am working on deals with out to use a functional Lock, or monitor structure, to provided exclusive access to only one member on separate threads. Below, is my class definition of the monitor (note that it is different than the actual monitor class given by c# library). What I am trying to do is make pictureboxes appear or disappear on my form.

I have attempted to add in an instance of the form so I can access the individual pictureboxes, however, my program seems to freeze.

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

Below is my form code, as you can see here, i try to create three seperate threads, one for each smoker.

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

    }

  }

}

Shray
  • 43
  • 2
  • 10
  • possible duplicate of [How to update the GUI from another thread in C#?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) – Erik Funkenbusch Oct 22 '13 at 00:23
  • You shouldn't be sleeping inside of a lock. And why do you call agent? – Millie Smith Oct 22 '13 at 00:39
  • I sleep to simulate the process "smoking". I call the agent so that after the smoker is done smoking, it notifies the agent to allow someone else to smoke. – Shray Oct 22 '13 at 00:41
  • you're calling one.agent() in the main code right now. I don't think you want to call it there. Ok, but sleep right after you release the lock. Sleeping inside the lock means no one else can access the lock. Or is that what you want? – Millie Smith Oct 22 '13 at 00:43
  • My one.agent() is the part of the code that allows one of the smokers to be called on, so they can smoke. Why wouldnt I want it in the main code? I only have the process sleep for 5 seconds, and within that 5 seconds, no one else should be able to "smoke." does that make sense? – Shray Oct 22 '13 at 00:46
  • Looks like you call smoker 1,2 or 3 on agent and thats pretty much it. Then you spawn three threads, each of which runs a different smoker. Why? – Millie Smith Oct 22 '13 at 00:48
  • Ok, I guess your sleeping makes sense then. – Millie Smith Oct 22 '13 at 00:49
  • I am trying to use concurrent processing, and use the monitor.enter() and monitor.exit() methods so I have some experience in how they work. FOr simiplicity, I have used Lock, because I know they call on both of these functions internally(or so i've heard). I want to run each smoker on a different thread because I want them to use resource, and check to see if it is available or not. – Shray Oct 22 '13 at 00:52
  • Ok, I'm not sure if I understand you correctly. I don't think your monitor object has an enter and exit. Lock does call enter critical section and exit critical section. Doing those yourself is slightly more involved, but doable. You are running each smoker on a different thread, even without the agent call – Millie Smith Oct 22 '13 at 01:00
  • Ok, maybe I could use some help on the monitor part too. Where do I even start now ... – Shray Oct 22 '13 at 01:09
  • I'm going to make a chat room when I get to my desk in a minute – Millie Smith Oct 22 '13 at 01:11
  • That would be great, but it looks like I cant enter because I dont have enough reputation. – Shray Oct 22 '13 at 01:12
  • @user2805001 http://chat.stackoverflow.com/rooms/39695/multithreading-user2805001-and-millismith – Millie Smith Oct 22 '13 at 01:19
  • Chat works, except for the part where I cant type into it. – Shray Oct 22 '13 at 01:21
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39696/discussion-between-user2805001-and-millie-smith) – Shray Oct 22 '13 at 01:22
  • Hey I just got back home, Im going to open the chat room (same as yesterdays) just shoot me a message when you get a chance. – Shray Oct 23 '13 at 00:15
  • I keep on running my program, and from what Im seeing, it doesnt seem to be running in sequence at all. Definitely not smoker1->smoker2->smoker3 either. The problem is, I dont even have agent running. – Shray Oct 23 '13 at 18:37
  • Are you going to be able to get on tonight? – Shray Oct 24 '13 at 01:33
  • @MillieSmith theres one thing thats completely stumping me, I wanted to ask you. Are you free? – Shray Oct 24 '13 at 21:30
  • I invited you on gmail, I'll just wait until you get on/accept. – Shray Oct 26 '13 at 22:38
  • @MillieSmith hey are you on ? – Shray Oct 28 '13 at 02:39
  • @MillieSmith got some time? – Shray Oct 29 '13 at 03:38
  • Yep in about 20 minutes. – Millie Smith Oct 29 '13 at 03:40

2 Answers2

0

my program seems to freeze

My one.agent() is the part of the code that allows one of the smokers to be called on, so they can smoke. Why wouldnt I want it in the main code?

Because you shouldn't be using Sleep() from the main UI thread, which is what happens when you call one.agent() from the Button Click event. When Sleep(5000) is hit, you're telling the forms main UI thread to not do ANYTHING for five seconds, thus the freezing you're seeing.

To fix this, you'd need agent() to execute smoker1(), smoker2(), or smoker3() in a separate thread like you're doing down below.

There are several other problems with the code, however, that must also be addressed before you can "fix" your code...

The next problem lies in you creating a new instance of Form1() inside your monitor() class. This instance of Form1() is not the same one that is visible on your screen. Acting upon it is modifying an invisible form that has never even been shown. To act upon the form that is actually visible on your screen would require you to either (a) pass a reference to it into your monitor() class when you create it, or (b) have your monitor() class raise custom events that Form1() subscribes to, again when it creates monitor().

The last problem lies in you attempting to change UI controls from within a thread other than the main UI thread. This will result in a cross-thread exception (unless you've turn this off, which you shouldn't). There are various ways to overcome this problem, the most basic of which involves using delegates and the Invoke() method of the Form/Control to which you are trying to update.

Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
0

After implementing this, I went looking for the Smoker Thread Problem that it looks like OP is trying to implement. This code should be easily adaptable to that problem.

The reason your UI is freezing is that you're calling one.agent() without putting it in a new thread. one.agent() sleeps, which keeps your UI from processing events.

OK, I've implemented some code to do the smoker problem with labels. Obviously it could be improved, for instance by not coupling the form to the threads.

I put two different locking mechanisms in, and left one commented out.

Essentially, there are three labels that can either be "Smoking" or "Not Smoking". The main UI thread creates three threads:

  1. Smoker1
  2. Smoker2
  3. Smoker3

Each of the threads continually tries to take the lock in a while loop. When they take the lock, they set their label to "Smoking", wait a few seconds, and then set their label to "Not Smoking". This uses thread safe code from 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 });
        }
    }
}
Community
  • 1
  • 1
Millie Smith
  • 4,536
  • 2
  • 24
  • 60