[英]How to make TaskCompletionSource.Task complete using specific TaskScheduler
How to make the completion of TaskCompletionSource.Task
happen on specific TaskScheduler
, when I call TaskCompletionSource.SetResult
? 当我调用
TaskCompletionSource.SetResult
时,如何使TaskCompletionSource.Task
的完成发生在特定的TaskScheduler
?
Currently, I'm using the idea I borrowed from 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();
}
So whenever I would return TaskCompletionSource.Task
to the caller, I now return TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)
instead. 因此,无论何时我将
TaskCompletionSource.Task
返回给调用者,我现在都改为返回TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)
。
Is it possible to somehow avoid this another level of indirection of ContinueWith
? 是否有可能以某种方式避免
ContinueWith
另一种间接访问级别?
It would be interesting to know your goals behind this. 知道您的目标背后很有趣。 Anyway, if you like to avoid the overhead of
ContinueWith
(which I think is quite low), you'd probably have to come up with your own version of a pattern similar to TaskCompletionSource
. 无论如何,如果您想避免
ContinueWith
的开销(我认为这很低),则可能必须提出自己的类似于TaskCompletionSource
模式的版本。
It's not that complex. 没那么复杂。 Eg, something like
Promise
below can be used in the same way you use TaskCompletionSource
, but would allow to provide a custom TaskScheduler
for completion (disclaimer: almost untested): 例如,可以使用与使用
TaskCompletionSource
相同的方式使用下面的Promise
东西,但是将允许提供自定义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);
}
}
On a side note, this version has an override for SetException(ExceptionDispatchInfo edi)
, so you could propagate the active exception's state from inside catch
: 附带说明一下,此版本具有
SetException(ExceptionDispatchInfo edi)
的替代,因此您可以从catch
内部传播活动异常的状态:
catch(Exception ex)
{
var edi = ExceptionDispatchInfo.Capture(ex);
promise.SetException(edi);
}
It's easy to create a generic version of this, too. 创建通用版本也很容易。
There's a downside of this approach, though. 但是,这种方法有一个缺点。 A 3rd party can do
promise.Task.Run
or promise.Task.RunSynchronously
, as the Task
is exposed in the TaskStatus.Created
state. 第三方可以执行
promise.Task.Run
或promise.Task.RunSynchronously
,因为Task
处于TaskStatus.Created
状态。
You could add a check for that into InvokeCompletionAction
, or you could probably hide it using nested tasks / Task.Unwrap
(although the latter would bring some overhead back). 您可以在
InvokeCompletionAction
添加InvokeCompletionAction
的检查,或者可以使用嵌套的任务/ Task.Unwrap
隐藏它(尽管后者会带来一些开销)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.