创建冰冷的TaskCompletionSource?

时间:2015-02-04 10:58:47

标签: c# .net task-parallel-library async-await

我正在编写一个库,其中包含基于.Net Tasks的调度功能(不是标准TaskSchedulerIScheduler ...)。我正在使用TaskCompletionSourceTask.Status对于表示基础操作的状态至关重要,包括 TaskStatus.Created,即已创建但尚未启动。我知道返回的任务通常应该很热,但对于我手动控制的代理任务,我确实最初希望它们为Created

不幸的是,TaskCompletionSource.Task的初始状态为WaitingForActivation,即它已经过了Created。换句话说,TaskCompletionSource支持两个状态,但我需要 三个 状态:

问题:如何获得可以手动设置为 三个 不同状态的Task?即Task.Status可以设置为:

1)Created
2)WaitingForActivation / WaitingForChildrenToComplete / WaitingToRun / Running之一 3)RanToCompletion / Canceled / Faulted

中的任何一个

下面的代码可以理解为抱怨类型不匹配。我可以通过将new Task<TResult>更改为new Task<Task<TResult>>来包装任务,但是要回到Task<TResult>我必须Unwrap()它,并且展开的任务将具有状态{{1 ,让我回到原点。

我将拥有大量这些内容,因此不能选择阻止每个WaitingForActivation的帖子。

我考虑过从Wait()继承并覆盖成员(使用new),但如果可能的话,为库用户提供实际的Task代替Task会很好因为我在许多其他地方也提出了常规任务。

想法?

DerivedTask

错误:
*无法将lambda表达式转换为委托类型'System.Func',因为块中的某些返回类型不能隐式转换为委托返回类型
*无法将类型'System.Threading.Tasks.Task'隐式转换为'TResult'

2 个答案:

答案 0 :(得分:6)

虽然我同意@ usr在评论中的观点,但从技术上讲,您仍然可以拥有提供这些状态的实现:

  1. Created
  2. WaitingToRun
  3. RanToCompletion/Canceled/Faulted
  4. 为了避免使用Task.Wait阻止线程,您可以使用内部帮助器TaskScheduler,这将首先将任务从Created转换为WaitingToRun,最后转换为一个已完成的州。

    下面的代码说明了这个概念。它只是经过了非常轻微的测试,可能不是完全线程安全的,也许可以改进以在多个任务中共享FakeTaskScheduler的单个实例。

    public class ColdTaskCompletionSource
    {
        public sealed class FakeTaskScheduler : TaskScheduler
        {
            Task _task;
    
            public FakeTaskScheduler()
            {
            }
    
            protected override void QueueTask(Task task)
            {
                _task = task;
            }
    
            protected sealed override bool TryDequeue(Task task)
            {
                if (task != _task)
                    return false;
    
                _task = null;
                return true;
            }
    
            protected override IEnumerable<Task> GetScheduledTasks()
            {
                if (_task == null)
                    yield break;
                yield return _task;
            }
    
            protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
            {
                return false;
            }
    
            public override int MaximumConcurrencyLevel
            {
                get { return 1; }
            }
    
            public bool Execute()
            {
                if (_task == null)
                    return false;
    
                var task = _task;
                _task = null;
                return base.TryExecuteTask(task);
            }
        }
    
        readonly Task _task;
        readonly CancellationTokenSource _cts;
        readonly object _lock = new Object();
        readonly FakeTaskScheduler _ts = new FakeTaskScheduler();
        Action _completionAction = null;
    
        // helpers
    
        void InvokeCompletionAction()
        {
            if (_completionAction != null)
                _completionAction();
        }
    
        void Complete()
        {
            if (_task.Status != TaskStatus.WaitingToRun)
                throw new InvalidOperationException("Invalid Task state");
            _ts.Execute();
        }
    
        // public API
    
        public ColdTaskCompletionSource()
        {
            _cts = new CancellationTokenSource();
            _task = new Task(InvokeCompletionAction, _cts.Token);
        }
    
        public Task Task { get { return _task; } }
    
        public void Start()
        {
            _task.Start(_ts);
        }
    
        public void SetCompleted()
        {
            lock (_lock)
                Complete();
        }
    
        public void SetException(Exception ex)
        {
            lock (_lock)
            {
                _completionAction = () => { throw ex; };
                Complete();
            }
        }
    
        public void SetCancelled()
        {
            lock (_lock)
            {
                _completionAction = () =>
                {
                    _cts.Cancel();
                    _cts.Token.ThrowIfCancellationRequested();
                };
                Complete();
            }
        }
    }
    

答案 1 :(得分:2)

你不能以合理的方式。

Task状态在内部处理,手动创建任务的唯一API使用TaskCompletionSource。您也无法继承Task,因为Status属性只是一个获取者,因为它是内部的,您无法到达支持字段m_stateFlags

然而,不合理的方法是创建一个等待TaskCompletionSource的任务,并控制任务的状态控制TaskCompletionSource

var taskCompletionSource = new TaskCompletionSource<bool>();
var cancellationTokenSource = new CancellationTokenSource();
var task = new Task(() => taskCompletionSource.Task.Wait(cancellationTokenSource.Token), cancellationTokenSource.Token); // task.Status == TaskStatus.Created

task.Start(); // task.Status == TaskStatus.Running

taskCompletionSource.SetResult(false) // task.Status == TaskStatus.RanToCompletion

或者

taskCompletionSource.SetException(new Exception("")) // task.Status == TaskStatus.Faulted

或者

cancellationTokenSource.Cancel() // task.Status == TaskStatus.Cancelled

我并不是真的建议你那样做。我不确定您为什么要控制Task的状态,但您可能需要创建自己可以自行管理的构造,而不是强迫Task进入设计它&# 39; t意为。