Marcin
Marcin

Reputation: 151

Changing some Dispatcher.BeginInvoke functionality into that of Dispatcher.Invoke

The reason for what I'm going to ask here is that Dispatcher.Invoke throws a TaskCanceledException when another thread executes Dispatcher.InvokeShutdown(). Dispatcher.BeginInvoke() does not seem to suffer from this and I wanted to move my codebase from using Dispatcher.Invoke(...) to Dispatcher.BeginInvoke(...). And below I wanted to ask if the following two Work() methods both running on a separate background thread are equivalent? (do any of you see any issues changing the first into the second?):

Work(){
   Dispatcher.Invoke(() => {sameFunction()});

   //more work
   ...
}

Work(){
   var task = Dispatcher.BeginInvoke((Action)(() =>  {sameFunction()});
   task.Wait();

   //more work
   ...
}

This issue is a direct consequence of the following issue's answer not having functioned as hoped for. It seems that once Dispatcher.InvokeShutdown has been called (once Dispatcher.HasShutdownStarted is true), all calls to Dispatcher.Invoke will end in throwing a TaskCancelledException.

Upvotes: 0

Views: 659

Answers (1)

BionicCode
BionicCode

Reputation: 28948

You should use Dispatcher.InvokeAsync instead of Dispatcher.BeginInvoke. BeginInvoke is part of the old API.
Also, never call Wait(), but use await:

await Dispatcher.InvokeAsync()

Using the new API also allows you to cancel operations (Invoke and InvokeAsync only): to fix the exception issue, you should provide a shared CancellationToken, that you associate with the Dispatcher (virtually, with the one you expect to get shutdown), to every invocation.
This way you can cancel the pending and running operations gracefully before you shutdown the Dispatcher.

Dispatcher.InvokeShutdown will abort any running Dispatcher operation, which applies to every synchronous Dispatcher.Invoke execution - hence the TaskCanceledException is being thrown on abortion.
Regarding Dispatcher.InvokeAsync (and Dispatcher.BeginInvoke): your probably don't experience the cancellation exception, because the dispatched operation is still pending due to its asynchronous execution.

This leads to the difference of both Dispatcher invocation examples:

Your first example uses Dispatcher.Invoke. From the documentation you could learn that it executes synchronously. This means, the delegate is executed immediately (pushed to the front of the queue - respecting the assigned priorities).

The second example uses Dispatcher.BegingInvoke (same applies to the modern Dispatcher.InvokeAsync). This method invokes the delegate asynchronously. This means, the delegate is enqueued into the dispatcher queue and executed after all preceeding pending operations in this queue are completed. If you don't await the call, the Dispatcher returns immediately after enqueuing the delegate and execution continues (this is the asynchronous part).

Therfore, the examples are not equivalent. You have to decide if postponing the delegate's execution is reasonable (Dispatcher.InvokeAsync) or not (Dispatcher.Invoke).

Upvotes: 1

Related Questions