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

C# 如何使用特定的TaskScheduler使TaskCompletionSource.Task完成,c#,.net,multithreading,task-parallel-library,C#,.net,Multithreading,Task Parallel Library,当我调用TaskCompletionSource.SetResult时,如何在特定的TaskScheduler上完成TaskCompletionSource.Task 目前,我正在使用我借鉴的想法: 静态公共任务ContinueOnTaskScheduler( 此任务(此,任务调度程序) { return@this.ContinueWith( 先行项=>先行项, 取消令牌。无, TaskContinuationOptions.Executes同步执行, 调度器)。展开(); } 因此,每当我将

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

目前,我正在使用我借鉴的想法:

静态公共任务ContinueOnTaskScheduler(
此任务(此,任务调度程序)
{
return@this.ContinueWith(
先行项=>先行项,
取消令牌。无,
TaskContinuationOptions.Executes同步执行,
调度器)。展开();
}
因此,每当我将
TaskCompletionSource.Task
返回给调用者时,我现在将返回
TaskCompletionSource.Task.ContinueOnTaskScheduler(scheduler)


有没有可能通过某种方式避免另一个层次的间接的
继续

了解你在这背后的目标会很有趣。无论如何,如果您想避免使用
ContinueWith
(我认为这是相当低的)的开销,您可能必须提出类似于
TaskCompletionSource
的模式的您自己的版本

没那么复杂。例如,下面类似于
Promise
的东西可以用与使用
TaskCompletionSource
相同的方式使用,但允许提供自定义的
TaskScheduler
来完成(免责声明:几乎未经测试):

另一方面,此版本具有对
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
,也可以使用嵌套的任务/code>Task.Unwrap
(尽管后者会带来一些开销)。

请注意,您同时指定了
TaskContinuationOptions.executes
调度程序
。您是否知道,只有当此
计划程序
与相同的同步上下文WARE
tcs关联时,它才会同步完成。调用了SetResult
?否则它会排队。我知道,没关系。谢谢你的指点。
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);
    }
}
catch(Exception ex)
{
    var edi = ExceptionDispatchInfo.Capture(ex);
    promise.SetException(edi);
}