我正在编写一个库,其中包含基于.Net Tasks的调度功能(不是标准TaskScheduler
,IScheduler
...)。我正在使用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()
它,并且展开的任务将具有状态{{1 ,让我回到原点。
我将拥有大量这些内容,因此不能选择阻止每个WaitingForActivation
的帖子。
我考虑过从Wait()
继承并覆盖成员(使用new),但如果可能的话,为库用户提供实际的Task
代替Task
会很好因为我在许多其他地方也提出了常规任务。
想法?
DerivedTask
错误:
*无法将lambda表达式转换为委托类型'System.Func',因为块中的某些返回类型不能隐式转换为委托返回类型
*无法将类型'System.Threading.Tasks.Task'隐式转换为'TResult'
答案 0 :(得分:6)
虽然我同意@ usr在评论中的观点,但从技术上讲,您仍然可以拥有提供这些状态的实现:
Created
WaitingToRun
RanToCompletion/Canceled/Faulted
为了避免使用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意为。