创建冰冷的 TaskCompletionSource?
Create ice cold TaskCompletionSource?
我正在编写一个包含基于 .Net Tasks 的调度功能(不是标准 TaskScheduler
、IScheduler
...)的库。我正在使用 TaskCompletionSource
,而 Task.Status
对于表示基础操作的状态至关重要, 包括 TaskStatus.Created
,即已创建但尚未开始。我知道 returned 任务通常应该很热,但是对于我手动控制的代理任务,我确实希望它们最初是 Created
.
对我来说很不幸,TaskCompletionSource.Task
的初始状态是 WaitingForActivation
,即它已经超过 Created
。换句话说,TaskCompletionSource
支持两个状态,但我需要三个状态:
问题:我怎样才能得到一个可以手动设置为three[的Task
不同的州? IE。 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
,特别是因为我提出了在许多其他地方也等待的常规任务。
想法?
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',因为块中的某些 return 类型不能隐式转换为委托 return 类型
* 无法将类型 'System.Threading.Tasks.Task' 隐式转换为 'TResult'
你不能以合理的方式。
Task
状态是在内部处理的,唯一用于手动创建任务的 API 是使用 TaskCompletionSource
。您也不能从 Task
继承,因为 Status
属性 只是一个 getter 并且您无法访问支持字段 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
进入它不适合的设计。
虽然我同意@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();
}
}
}
我正在编写一个包含基于 .Net Tasks 的调度功能(不是标准 TaskScheduler
、IScheduler
...)的库。我正在使用 TaskCompletionSource
,而 Task.Status
对于表示基础操作的状态至关重要, 包括 TaskStatus.Created
,即已创建但尚未开始。我知道 returned 任务通常应该很热,但是对于我手动控制的代理任务,我确实希望它们最初是 Created
.
对我来说很不幸,TaskCompletionSource.Task
的初始状态是 WaitingForActivation
,即它已经超过 Created
。换句话说,TaskCompletionSource
支持两个状态,但我需要三个状态:
问题:我怎样才能得到一个可以手动设置为three[的Task
不同的州? IE。 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
,特别是因为我提出了在许多其他地方也等待的常规任务。
想法?
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',因为块中的某些 return 类型不能隐式转换为委托 return 类型
* 无法将类型 'System.Threading.Tasks.Task' 隐式转换为 'TResult'
你不能以合理的方式。
Task
状态是在内部处理的,唯一用于手动创建任务的 API 是使用 TaskCompletionSource
。您也不能从 Task
继承,因为 Status
属性 只是一个 getter 并且您无法访问支持字段 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
进入它不适合的设计。
虽然我同意@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();
}
}
}