I've got an application with multiple Dispatcher
s (aka GUI threads, aka message pumps) to ensure that a slow, unresponsive portion of the GUI runs without affecting the rest of the application too heavily. I also use Task
a lot.
Currently I've got code that conditionally runs an Action
on a TaskScheduler
or a Dispatcher
and then returns a Task
either directly or by manually creating one using TaskCompletionSource
. However, this split personality design makes dealing with cancellation, exceptions etc. all much more complicated than I'd like. I want to use Task
s everywhere and DispatcherOperation
s nowhere. To do that I need to schedule tasks on dispatchers - but how?
How can I get a TaskScheduler
for any given Dispatcher
?
Edit: After the discussion below, I settled on the following implementation:
public static Task<TaskScheduler> GetScheduler(Dispatcher d) {
var schedulerResult = new TaskCompletionSource<TaskScheduler>();
d.BeginInvoke(() =>
schedulerResult.SetResult(
TaskScheduler.FromCurrentSynchronizationContext()));
return schedulerResult.Task;
}
Step 1: Create an extension method:
public static Task<TaskScheduler> ToTaskSchedulerAsync (
this Dispatcher dispatcher,
DispatcherPriority priority = DispatcherPriority.Normal) {
var taskCompletionSource = new TaskCompletionSource<TaskScheduler> ();
var invocation = dispatcher.BeginInvoke (new Action (() =>
taskCompletionSource.SetResult (
TaskScheduler.FromCurrentSynchronizationContext ())), priority);
invocation.Aborted += (s, e) =>
taskCompletionSource.SetCanceled ();
return taskCompletionSource.Task;
}
Step 2: Use the extension method:
Old syntax:
var taskSchedulerAsync = Dispatcher.CurrentDispatcher.ToTaskSchedulerAsync ();
var taskFactoryAsync = taskSchedulerAsync.ContinueWith<TaskFactory> (_ =>
new TaskFactory (taskSchedulerAsync.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
// this is the only blocking statement, not needed once we have await
var taskFactory = taskFactoryAsync.Result;
var task = taskFactory.StartNew (() => { ... });
New syntax:
var taskScheduler = await Dispatcher.CurrentDispatcher.ToTaskSchedulerAsync ();
var taskFactory = new TaskFactory (taskScheduler);
var task = taskFactory.StartNew (() => { ... });