给定SynchronizationContext
,我已经拥有(并且基本上是特定线程的窗口),如何创建发布到此上下文的Task
?
作为参考,这里有一个非常基本的演示如何设置SynchronizationContext
。
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;
}
}
这适用于已在上下文中运行的异步 / 等待。
现在,我希望能够从外部上下文(例如,从UI线程)向此发布Task
,但似乎无法找到干净的方法。
一种方法是使用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;
});
但这是重新发明轮子和支持变体的主要痛苦,例如StartNew(Func<TResult>)
,StartNew(Func<Task<TResult>>)
等。
SynchronizationContext
的{{3}}接口可能是理想的,但我似乎无法干净地实例化它:
TaskFactory CreateTaskFactory()
{
var original = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(SomeDispatcher); // yuck!
try
{
return new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
}
finally
{
SynchronizationContext.SetSynchronizationContext(original);
}
}
(即必须临时软管当前线程的同步上下文似乎很麻烦。)
答案 0 :(得分:5)
似乎默认SynchronizationContextTaskScheduler
是
但它的源代码可用here,我们看到它相对简单,所以我们可以尝试推出自己的调度程序,如下所示:
public sealed class MySynchronizationContextTaskScheduler : TaskScheduler {
private readonly SynchronizationContext _synchronizationContext;
public MySynchronizationContextTaskScheduler(SynchronizationContext context) {
_synchronizationContext = context;
}
[SecurityCritical]
protected override void QueueTask(Task task) {
_synchronizationContext.Post(PostCallback, task);
}
[SecurityCritical]
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
if (SynchronizationContext.Current == _synchronizationContext) {
return TryExecuteTask(task);
}
else
return false;
}
[SecurityCritical]
protected override IEnumerable<Task> GetScheduledTasks() {
return null;
}
public override Int32 MaximumConcurrencyLevel
{
get { return 1; }
}
private void PostCallback(object obj) {
Task task = (Task) obj;
base.TryExecuteTask(task);
}
}
然后您的CreateTaskFactory
变为:
TaskFactory CreateTaskFactory() {
return new TaskFactory(new MySynchronizationContextTaskScheduler(SomeDispatcher));
}
您可以使用以下方式创建任务:
var factory = CreateTaskFactory();
var task = factory.StartNew(...);
答案 1 :(得分:3)
Parallel Extensions Extras contains SynchronizationContextTaskScheduler
完全符合您的要求。
如果您不想自己编译PEE,there is an unofficial NuGet package for it。
请注意,您通常不需要这样做,而且您要求这样做的事实可能表明您的设计存在缺陷。