Gishu
Gishu

Reputation: 136643

Why does the BackgroundWorker not call the RunWorkerCompleted on the right thread in this unit test?

The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app.

However in my test, the callback is not invoked on the calling thread.

[Test]
public void TestCallbackIsInvokedOnClientThread()
{

     var clientId = Thread.CurrentThread.ManagedThreadId;
     int callbackThreadId = -1;
     var manualEvent = new ManualResetEventSlim(false);

     var someUIControl = new TextBox();
     var bw = new BackgroundWorker();

     bw.DoWork += (s,e) => e.Result = 5 ; // worker thread

     bw.RunWorkerCompleted += (s, e) =>
                                  {
                                      try
                                      {
                                          callbackThreadId = Thread.CurrentThread.ManagedThreadId;
                                          //someUIControl.Text = callbackThreadId.ToString();
                                          manualEvent.Set();
                                      }
                                      catch (System.Exception ex)
                                      {
                                          Console.Out.WriteLine(ex.ToString());
                                      }
                                  };
     bw.RunWorkerAsync();

     if (!manualEvent.Wait(5000))
         Assert.Fail("no callback");
     Assert.AreEqual(clientId, callbackThreadId);
 }

Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread

What am I missing ?

In the Unit Test I see behavior like

------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9

In the Wpf App, this would be

MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1

Update: With Justin's answer, made the following changes and now the test passes

.

for (int i = 0; i < 3; i++)
{
    control.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
    Thread.Sleep(50);
}

Upvotes: 7

Views: 2185

Answers (4)

centellenc
centellenc

Reputation: 21

In my case I am using Windows Forms and controls don't have a Dispatcher property (see the answer in no definition for dispatcher).

Gishu's solution works as well if we use Dispatcher.CurrentDispatcher instead of the one in the control.

On test initialisation:

// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher; 

And then when waiting for the background task to be completed:

_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);

Upvotes: 0

Dave
Dave

Reputation: 1209

I ran into a problem in my code where the user closing a window caused a save, that in turn used a BackgroundWorker to update the home window and it did not run the RunWorkerCompleted because the thread that started the BackgroundWorker had terminated when the window closed.

I had to change the closing window's save run in the home window's context so that after the BackgroundWorker completed, it had a thread to return to.

Upvotes: 0

Justin Harvey
Justin Harvey

Reputation: 14682

The behavior is different dues to the different contexts that you are running under.

When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.

Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.

Upvotes: 10

Arthur Nunes
Arthur Nunes

Reputation: 7058

I belive that the calling thread must support messagepumping (mean, being STA apartment and having an associated Dispatcher) so the background worker can post the callback. If it does not, the background worker has no option but execute the callback in its own thread. If you want to test it, see this link.

Upvotes: 3

Related Questions