简体   繁体   English

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

[英]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.Runpromise.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM