Reputation: 151
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
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