Why BackgroundWorker always is busy?

Darf Zon picture Darf Zon · Jan 21, 2013 · Viewed 21.8k times · Source

I realized something strange in my background worker in my WPF application.

What I'm trying to accomplish right now is to wait until the BW finishes to start another thread.

Check the following code:

if (bw.IsBusy)
{
    bw.CancelAsync();

    System.Threading.ThreadStart WaitThread = 
        new System.Threading.ThreadStart(delegate() {
            while (bw.IsBusy)
            {
                System.Threading.Thread.Sleep(100);
            }

            bw.RunWorkerAsync();
        });

    System.Windows.Application.Current.Dispatcher.Invoke(
        System.Windows.Threading.DispatcherPriority.Normal,
        WaitThread);  // if I remove this line, bw fires RunWorkerAsyncEvent
}
else
{
    bw.RunWorkerAsync();
}

Please note that I added a Dispatcher.Invoke to wait until the bw is not busy, but ALL THE TIME IS BUSY if I Invoke it and never fires the RunWorkerAsyncCompleted event. Although,, if I remove that line, FIRES the event and it's really strange that.

How can I wait until the bw finishes?

Answer

Hans Passant picture Hans Passant · Jan 21, 2013

This is called "deadlock", a very common threading problem. The BGW cannot stop being busy until the RunWorkerCompleted event runs. That event runs on your app's main thread, it can only run when your main thread isn't busy doing something else. It has to be idle, running inside the dispatcher loop.

But your main thread isn't idle, it is stuck inside the while() loop waiting for IsBusy to return false. So the event can never run since the main thread is busy waiting for the BGW to complete, the BGW cannot complete because the main thread never goes idle. "Deadly embrace", aka deadlock.

You will have to do this differently, you cannot wait. Say by creating another instance of BGW. Or moving the code after the wait into the RunWorkerCompleted event handler. If it is activated by, say, a button's Click event then be sure to disable the button when call RunWorkerAsync(), re-enable it in RunWorkerCompleted.