如何使用特定的TaskScheduler完成TaskCompletionSource.Task

当我调用TaskCompletionSource.SetResult时,如何在特定的TaskScheduler上完成TaskCompletionSource.Task的完成?

目前,我正在使用我从这篇文章借来的想法:

 static public Task ContinueOnTaskScheduler( this Task @this, TaskScheduler scheduler) { return @this.ContinueWith( antecedent => antecedent, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler).Unwrap(); } 

因此,每当我将TaskCompletionSource.Task返回给调用者时,我现在返回TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)

是否有可能以某种方式避免ContinueWith另一个间接层次?

知道你背后的目标会很有趣。 无论如何,如果你想避免ContinueWith的开销(我觉得它很低),你可能不得不提出类似于TaskCompletionSource自己版本的模式。

它并不复杂。 例如,下面的Promise东西可以像使用TaskCompletionSource一样使用,但允许提供自定义的TaskScheduler来完成(免责声明:几乎未经测试):

 public class Promise { readonly Task _task; readonly CancellationTokenSource _cts; readonly object _lock = new Object(); Action _completionAction = null; // public API public Promise() { _cts = new CancellationTokenSource(); _task = new Task(InvokeCompletionAction, _cts.Token); } public Task Task { get { return _task; } } public void SetCompleted(TaskScheduler sheduler = null) { lock(_lock) Complete(sheduler); } public void SetException(Exception ex, TaskScheduler sheduler = null) { lock (_lock) { _completionAction = () => { throw ex; }; Complete(sheduler); } } public void SetException(System.Runtime.ExceptionServices.ExceptionDispatchInfo edi, TaskScheduler sheduler = null) { lock (_lock) { _completionAction = () => { edi.Throw(); }; Complete(sheduler); } } public void SetCancelled(TaskScheduler sheduler = null) { lock (_lock) { // don't call _cts.Cancel() outside _completionAction // otherwise the cancellation won't be done on the sheduler _completionAction = () => { _cts.Cancel(); _cts.Token.ThrowIfCancellationRequested(); }; Complete(sheduler); } } // implementation void InvokeCompletionAction() { if (_completionAction != null) _completionAction(); } void Complete(TaskScheduler sheduler) { if (Task.Status != TaskStatus.Created) throw new InvalidOperationException("Invalid task state."); _task.RunSynchronously(sheduler?? TaskScheduler.Current); } } 

另外,此版本具有SetException(ExceptionDispatchInfo edi)的覆盖,因此您可以从catch内部传播活动exception的状态:

 catch(Exception ex) { var edi = ExceptionDispatchInfo.Capture(ex); promise.SetException(edi); } 

创建一个通用版本也很容易。

但是,这种方法有一个缺点。 第三方可以执行promise.Task.Runpromise.Task.RunSynchronously ,因为TaskTaskStatus.Created状态下公开。

您可以在InvokeCompletionAction添加InvokeCompletionAction的检查,或者您可以使用嵌套任务/ Task.Unwrap隐藏它(尽管后者会带来一些开销)。