首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >制造冰冷TaskCompletionSource?

制造冰冷TaskCompletionSource?
EN

Stack Overflow用户
提问于 2015-02-04 10:58:47
回答 2查看 639关注 0票数 10

我正在编写一个包含调度功能的库(不是标准的TaskSchedulerIScheduler.)基于.Net任务。我正在使用TaskCompletionSource,而Task.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(),而未包装的任务将具有状态WaitingForActivation,这使我回到了起点。

我将有很多这样的工具,所以用Wait()阻止每个线程都不是一种选择。

我已经考虑过从Task继承和重写成员(使用新的),但如果可能的话,最好给库用户一个实际的Task而不是DerivedTask,特别是因为我介绍了许多其他地方也需要等待的常规任务。

想法?

代码语言:javascript
复制
private TaskCompletionSource<TResult> tcs;

private async Task<TResult> CreateStartCompleteAsync()
{
    await tcs.Task;
    if (tcs.Task.IsCanceled)
    {
        throw new OperationCanceledException("");
    }
    else if // etc.
}

public ColdTaskCompletionSource()
{
    tcs = new TaskCompletionSource<TResult>();
    Task = new Task<TResult>(() => CreateStartCompleteAsync());
}

错误:

*无法将lambda表达式转换为委托类型'System.Func‘,因为块中的某些返回类型不能隐式转换为委托返回类型

*无法隐式地将“System.Threading.Tasks.Task”转换为“TResult”

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-02-04 15:26:18

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

  1. Created
  2. WaitingToRun
  3. 要么是RanToCompletion/Canceled/Faulted

为了避免使用Task.Wait阻塞线程,您可以使用内部助手TaskScheduler,它首先将任务从Created转换为WaitingToRun,并最终转换为已完成的状态之一。

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

代码语言:javascript
复制
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();
        }
    }
}
票数 6
EN

Stack Overflow用户

发布于 2015-02-04 11:56:25

你不能以合理的方式。

Task状态是在内部处理的,手动创建任务的惟一API是使用TaskCompletionSource。您也不能从Task继承,因为Status属性只是一个getter,并且由于它是内部的,所以您无法到达备份字段m_stateFlags

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

代码语言:javascript
复制
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

代码语言:javascript
复制
taskCompletionSource.SetException(new Exception("")) // task.Status == TaskStatus.Faulted

代码语言:javascript
复制
cancellationTokenSource.Cancel() // task.Status == TaskStatus.Cancelled

我不是真的建议你这么做。我不知道您为什么要控制Task的状态,但是您可能需要创建自己的构造,您可以自己管理自己,而不是强迫Task进行它本来不适合的设计。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28319630

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档