views:

351

answers:

4

Imagine you have two buttons on the win form. What do you think should be the behavior when user presses the "button 1" with the below code?

Should it display all 5 message box in one go, or one by one - MessageBox.Show statement is inside a lock statement?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private static readonly object lockobject = new object();

    private void button1_Click(object sender, EventArgs e)
    {
        var action = new Action(function);
        for(int i = 0; i< 5; i++)
        {
            action.BeginInvoke(null, null);
        }
    }

    private void function()
    {
        if (button2.InvokeRequired)
        {
            var func = new Action(function);
            button2.Invoke(func);
        }
        else
        {
            lock (lockobject)
            {
                MessageBox.Show("Testing");
            }
        }
    }
}

Now if we replace MessageBox.Show with any other statment, it would execute the statement only one at a time, the other threads would wait, one at a time.

A: 

I suspect the UI thread is pumping messages during the MessageBox life-cycle. Because locks are re-entrant (and the UI thread is running the code each time), this causes the above. Perhaps try passing the owner (this) into the message-box? (I'll try in a sec...).

You could block it more forcefully, but that will block painting ("not responding" etc).

Marc Gravell
+4  A: 

Since your lock statement is executed when InvokeRequired is false, the locks will all run on the same (main) thread. Therefore the locks will not block.

If you want the MessageBox to block, use ShowDialog instead.

Jakob Christensen
There is no ShowDialog on MessageBox
Marc Gravell
+2  A: 
  1. lock only blocks if another thread owns the lock, locking on the same object from the same thread multiple times is allowed - otherwise it would be an instant deadlock, after all it would have been blocking the current thread while waiting for the current thread.

  2. Control.BeginInvoke doesn't execute code in a different thread, it will always execute the code in the thread pumping messages for the control, it does so by posting a message to the control's input queue and then executing the code when the message arrives.

because of 2 your code isn't multi-threaded at all, everything executes in the same thread - and this brings us back to 1, when you don't have multiple threads lock does nothing.

Nir
A: 

I agree with Nir. After you change your function to the one below, you can test that you are running on the same thread (not surprisingly):

private void function()
{
   if (button2.InvokeRequired)
   {
        var func = new Action(function);
        button2.Invoke(func);
   }
   else
   {
        lock (lockobject)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;
            MessageBox.Show("Testing. Running on thread "+threadId);
        }
    }
}

So here because your UI thread is owing the lock, it doesn't get blocked. The bottom line is that STA threads are not compatible with proper multithreaded programming.

Grzenio