问题描述
如何制作完成 TaskCompletionSource.Task
的发生在特定的的TaskScheduler
,当我打电话 TaskCompletionSource.SetResult
?
How to make the completion of TaskCompletionSource.Task
happen on specific TaskScheduler
, when I call TaskCompletionSource.SetResult
?
目前,我用我的这个帖子借来的想法:
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();
}
所以,每当我将返回 TaskCompletionSource.Task
给调用者,我现在回 TaskCompletionSource.Task.ContinueOnTaskScheduler(调度)
代替。
So whenever I would return TaskCompletionSource.Task
to the caller, I now return TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)
instead.
是否有可能以某种方式避免这种 ContinueWith
?
Is it possible to somehow avoid this another level of indirection of ContinueWith
?
推荐答案
这将是有趣的知道这背后你的目标。无论如何,如果你想避免 ContinueWith
的开销(我认为这是相当低的),你可能要拿出你自己的版本类似的模式 TaskCompletionSource
。
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
.
这不是那么复杂。例如,像无极
下面可以使用 TaskCompletionSource
以同样的方式被使用,但将允许提供自定义的TaskScheduler
完成(免责声明:几乎未经):
It's not that complex. E.g., 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):
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)
,所以你可以从抓内部传播活动异常的状态
:
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
:
catch(Exception ex)
{
var edi = ExceptionDispatchInfo.Capture(ex);
promise.SetException(edi);
}
可以很容易地创建这样的通用版本了。
It's easy to create a generic version of this, too.
有这种方法的缺点是,虽然。第三方可以做 promise.Task.Run
或 promise.Task.RunSynchronously
,因为工作
在 TaskStatus.Created
状态暴露出来。
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.
您可以添加该支票到 InvokeCompletionAction
,或者你可以用它嵌套的任务可能隐藏/ Task.Unwrap
(尽管后者会带来一些开销后)。
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).
这篇关于如何使用特定的TaskScheduler使TaskCompletionSource.Task完整的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!