gpt4 book ai didi

c# - 从 TaskScheduler 中取消 TPL 任务

转载 作者:行者123 更新时间:2023-11-30 18:26:35 27 4
gpt4 key购买 nike

我正在尝试创建一个 TaskScheduler它按顺序运行所有任务,但只会“完成”最近计划的任务。例如,如果我用它来安排任务 A,那么在它完成安排任务 B 和 C 之前,我希望只有 C 被认为是成功的。 A 可以继续其工作,但在完成时应被视为“已取消”,而 B 甚至在开始之前应标记为已取消。

我已经有了在线程池上按顺序执行委托(delegate)的现有代码,并管理了最多有 2 个排队任务的想法 - 一个当前正在执行,另一个正在执行。缺少的部分是能够将任务的结果状态设置为已取消

不幸的是,它似乎来自 TaskScheduler ,实际上您几乎无法访问 Task 的状态, 或任何 CancellationToken .

我试图通过跟踪最后排队的任务来解决这个问题,并在执行任务时抛出 TaskCancelledException如果它不等于最后排队的任务,但这似乎不起作用。我想这是因为任务的委托(delegate)中没有抛出异常,所有的“魔法”实际上都是在 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<>类是一个帮助程序,它通过事件回调以便在单个后台线程上处理排队的项目,只允许一个事件项目和一个待处理项目。

如果“最后一个任务”在处理器回调之前发生了变化,异常只会阻止任务运行(但不会影响其状态)。如果回调确实开始运行,但“最后一个任务”在此期间发生了变化,则抛出异常为时已晚,已经在任何延续已经开始之后。

我也完全不确定这个原因,但是第一次使用调度程序(我为每个 UI 元素单击安排一个任务),QueueTask在新任务中被调用一次。但是对于每个后续调度,它都会被调用两次。这把事情搞得更糟,因为 _LastTask被覆盖。

我觉得 TaskCompletionSource<>可能会有一些用处,但不太清楚如何。

是否有可能实现 TaskScheduler是否按描述工作?我知道我可以在创建任务时在调度程序之外实现这种行为,但我需要在很多地方使用它,并且我试图通过将它放在可重用的调度程序中来让生活更轻松。

最佳答案

我会创建一个辅助类,它接受一个输入操作,启动它,取消现有的操作,并覆盖它的内部变量。因为你不能执行 Cancel()明确而直接地在 Task<T> 上,您需要保留自己的 TaskCancellationSource便利。如果要提供外部 token ,可以将它们与 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:我还没有测试过这个,所以请仔细检查!

关于c# - 从 TaskScheduler 中取消 TPL 任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28390258/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com