如何使用特定的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.Run
或promise.Task.RunSynchronously
,因为Task
在TaskStatus.Created
状态下公开。
您可以在InvokeCompletionAction
添加InvokeCompletionAction
的检查,或者您可以使用嵌套任务/ Task.Unwrap
隐藏它(尽管后者会带来一些开销)。