当我致电TaskCompletionSource.Task
时,如何在特定TaskScheduler
上完成TaskCompletionSource.SetResult
?
目前,我正在使用我从this post借来的想法:
static public Task<TResult> ContinueOnTaskScheduler<TResult>(
this Task<TResult> @this, TaskScheduler scheduler)
{
return @this.ContinueWith(
antecedent => antecedent,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
scheduler).Unwrap();
}
所以每当我将TaskCompletionSource.Task
返回给调用者时,我现在返回TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)
。
是否有可能以某种方式避免ContinueWith
答案 0 :(得分:1)
知道你背后的目标会很有趣。无论如何,如果你想避免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
内传播活动异常的状态:
catch(Exception ex)
{
var edi = ExceptionDispatchInfo.Capture(ex);
promise.SetException(edi);
}
创建这个的通用版本也很容易。
但是,这种方法的缺点是。第三方可以promise.Task.Run
或promise.Task.RunSynchronously
,因为Task
已在TaskStatus.Created
州公开。
你可以在InvokeCompletionAction
中添加对它的检查,或者你可以使用嵌套任务/ Task.Unwrap
隐藏它(虽然后者会带来一些开销)。