Given a SynchronizationContext
, which I already have (and is basically a window to a specific thread), how do I create Task
s that are posted to this context?
For reference, here's a very basic demonstration of how the SynchronizationContext
is set up.
public class SomeDispatcher : SynchronizationContext
{
SomeDispatcher() {
new Thread(() => {
SynchronizationContext.SetSynchronizationContext(this);
// Dispatching loop (among other things)
}).Start();
}
override void Post(SendOrPostCallback d, object state)
{
// Add (d, state) to a dispatch queue;
}
}
This works fine for async / awaits that are already running in the context.
Now, I want to be able to post Task
s to this from an outside context (e.g. from a UI thread) but can't seem to find a clean way of doing this.
One way to do this is by using TaskCompletionSource<>
.
Task StartTask(Action action)
{
var tcs = new TaskCompletionSource<object>();
SaidDispatcher.Post(state => {
try
{
action.Invoke();
tcs.SetResult(null);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
});
But this is reinventing the wheel and a major pain supporting variations such as StartNew(Func<TResult>)
, StartNew(Func<Task<TResult>>)
, etc.
A TaskFactory
interface to the SynchronizationContext
is probably ideally, but I can't seem to instantiate one cleanly:
TaskFactory CreateTaskFactory()
{
var original = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(SomeDispatcher); // yuck!
try
{
return new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
}
finally
{
SynchronizationContext.SetSynchronizationContext(original);
}
}
(i.e. Having to temporary hose the current thread's synchronization context seems hacky.)