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

时间:2014-03-28 05:01:23

标签: c# .net multithreading task-parallel-library

当我致电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

的另一个间接层次

1 个答案:

答案 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.Runpromise.Task.RunSynchronously,因为Task已在TaskStatus.Created州公开。

你可以在InvokeCompletionAction中添加对它的检查,或者你可以使用嵌套任务/ Task.Unwrap隐藏它(虽然后者会带来一些开销)。