views:

99

answers:

3

I have a Windows Forms application with a BackgroundWorker. In a method on the main form, a MessageBox is shown and the user must click the OK button to continue. Meanwhile, while the messagebox is being displayed, the BackgroundWorker finishes executing and calls the RunWorkerCompleted event. In the method I have assigned to that event, which runs on the UI thread, the Close method is called on the form. Even though the method that shows the message box is still running, the UI thread is not blocking other threads from invoking methods on it. So the Close method gets called on the form. What I want is for the UI thread to block other threads' invokes until the method with the message box has finished. Is there an easy way to do that?

A: 

Use a ManualResetEvent to control synchronization between the RunWorkCompleted event and the method displaying the MessageBox.

Patrick Steele
A: 

You can use a simple lock for this. In your form, create a form-level member like this:

private object _locker = new object();

Then have your code acquire the lock in both methods, like this:

private void RunWorkerCompleted()
{
    lock (_locker)
    {
        this.Close();
    }
}

private void ShowSomeMessage()
{
    lock (_locker)
    {
        MessageBox.Show("message");
    }
}

If your code is currently blocking in ShowSomeMessage() (i.e. the message box is still open), the RunWorkerCompleted() method will wait until the message box is closed (releasing the lock) before closing the form.

Update: this should work, regardless of which thread is calling what:

private bool _showingMessage = false;

Then have your code acquire the lock in both methods, like this:

private void RunWorkerCompleted()
{
    while (_showingMessage)
    {
        Thread.Sleep(500); // or some other interval (in ms)
    }
    this.Close();
}

private void ShowSomeMessage()
{
    _showingMessage = true;
    MessageBox.Show("message");
    _showingMessage = false;
}
MusiGenesis
This is what I initially tried. But it did not work because, I believe, the two methods run on the same thread. And when RunWorkerCompleted() gets to the lock statement, it does not block because the thread it runs on already has the lock. Please correct me if I am wrong.
YWE
That doesn't sound right, unless one of these methods is being called via `Invoke` or `BeginInvoke`. If the message box is being shown from a button click, that would be on the UI thread, which should be different from the background worker thread.
MusiGenesis
I tried the 2nd way too. It sort of works. But it suffers from the problems Stewert mentioned. The UI thread cannot respond to mouse clicks and so the user cannot click OK. It results in a sort of "deadlock".
YWE
+1  A: 

I think that you should be more disciplined about how your events are being handled. You don't want to stop methods being invoked on the UI thread, because the method invocation mechanism is the same as the one that allows things like mouse and keyboard events to work. If you block method invocation you will also block that. Instead you need to delay the methods somehow. You may need to write your own synchronization code to do this.

Stewart