我试图创建一个按顺序运行所有任务的TaskScheduler
,但只会完成'最近安排的任务。例如,如果我使用它来安排任务A,那么在它完成任务B和C的计划之前,我只希望C被认为是成功的。 A可以继续工作,但应该考虑取消'在完成时,B应该在它开始之前被标记为取消。
我已经获得了在线程池上顺序执行委托的现有代码,并且管理了最多有2个排队任务的想法 - 一个当前正在执行,另一个是下一个。缺少的部分是能够将任务的结果状态设置为已取消
不幸的是,从TaskScheduler
开始,您实际上很少能够访问Task
或任何CancellationToken
的状态。
我试图通过跟踪最后排队的任务来解决这个问题,并且在执行任务时,如果TaskCancelledException
不等于最后排队的任务,则抛出TryExecuteTask()
,但这似乎不起作用。我想这是因为任务的代表内部并没有抛出异常,而且所有的魔法都被排除在外。实际上是在public class CurrentPendingTaskScheduler : TaskScheduler
{
private readonly ThreadSafeCurrentPendingQueueProcessor<Task> _Processor;
private Task _LastTask;
public CurrentPendingTaskScheduler()
{
_Processor = new ThreadSafeCurrentPendingQueueProcessor<Task>();
_Processor.Process += _Processor_Process;
}
private void _Processor_Process(Task obj)
{
// If there's a newer task already, cancel this one before starting
if (obj != _LastTask)
throw new TaskCanceledException(obj);
TryExecuteTask(obj);
// If a newer task was added whilst we worked, cancel this one
if (obj != _LastTask)
throw new TaskCanceledException(obj);
}
protected override void QueueTask(Task task)
{
_LastTask = task;
_Processor.Enqueue(task);
}
protected override Boolean TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued)
{
return false;
}
protected override IEnumerable<Task> GetScheduledTasks()
{
throw new NotImplementedException();
}
}
内处理的。
以下是我所拥有的:
ThreadSafeCurrentPendingQueueProcessor<>
QueueTask
类是一个帮助器,它通过一个事件回调,以便处理单个后台线程上的入队项目,只允许一个活动项目和一个待处理项目。
如果&#39;最后一项任务&#39;在处理器回调之前已经改变,该异常只是阻止任务运行(但不会影响其状态)。如果回调确实可以运行但是最后一个任务&#39;在此期间发生了变化,在任何延续开始之后,异常就已经太晚了。
此外我根本不确定是什么原因,但是第一次使用调度程序(我为每个UI元素点击安排一个任务),_LastTask
被调用一次,新任务。然而,对于每个后续调度,它被调用两次。随着TaskCompletionSource<>
被覆盖,这会使事情进一步混乱。
我觉得TaskScheduler
可能有用,但不太清楚。
是否可以实现按照描述工作的{{1}}?我知道我可以在调度程序之外,在我创建任务的时候实现这种行为,但我需要在很多地方使用它,并试图通过将它放在可重用的调度程序中来使生活更轻松。 / p>
答案 0 :(得分:0)
我会创建一个辅助类,它接受输入操作,启动它,取消现有的操作,并覆盖其内部变量。由于您无法在Cancel()
上明确直接执行Task<T>
,因此您需要保留自己的TaskCancellationSource
。如果你想提供一个外部令牌,你可以将它们与CancellationTokenSource.CreateLinkedTokenSource(...)
结合使用。如果你需要关注结果,这将为TaskCompletionSource提供一个很好的机会。
public class OverwriteTaskHandler<T>
{
private Task<T> _task;
private TaskCompletionSource<T> _tcs;
private CancellationTokenSource _cts;
public OverwriteTaskHandler(Func<T> operation)
{
_tcs = new TaskCompletionSource<T>();
_cts = new CancellationTokenSource();
TryPushTask(operation);
}
public bool TryPushTask(Func<T> operation)
{
if (_tcs.Task.IsCompleted)
return false; //It would be unsafe to use this instance as it is already "finished"
_cts.Cancel();
_cts = new CancellationTokenSource();
_task = Task.Run(operation, _cts.Token);
_task.ContinueWith(task => _tcs.SetResult(task.Result));
return true;
}
public void Cancel()
{
_cts.Cancel();
}
public Task<T> WrappedTask { get { return _tcs.Task; } }
}
Discalimer:我没有对此进行过测试,所以请仔细检查一下!
答案 1 :(得分:0)
AFAIK TaskScheduler无法用于完成这项工作。你真的需要别的东西。例如,您可以编写一个具有以下用法的帮助程序类:
static CurrentPendingTaskContext ctx = ...;
async Task MyAsyncFunc() {
await ctx.RegisterAndMaybeCancel();
try {
//rest of method
}
finally {
ctx.NotifyCompletion();
}
}
RegisterAndMaybeCancel
将等到当前正在运行的任务完成。如果此特定任务已被另一个任务取代,则会抛出取消异常。
我现在没有时间实施该课程(尽管它很诱人)。但我认为这种语法模式很简单,你可以在很多地方使用它。
您也可以使用IDisposable
模式来摆脱finally:
async Task MyAsyncFunc() {
using (await ctx.RegisterAndMaybeCancel())
{
//rest of method
}
}