我使用的是具有async
方法但没有CancellationToken
重载的外部库。
现在我正在使用另一个StackOverflow问题的扩展方法来添加CancellationToken
:
public async static Task HandleCancellation(this Task asyncTask, CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested. http://stackoverflow.com/a/18672893/1149773
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(() =>
tcs.TrySetCanceled(), useSynchronizationContext: false);
Task cancellationTask = tcs.Task;
// Create a task that completes when either the async operation completes, or
// cancellation is requested.
Task readyTask = await Task.WhenAny(asyncTask, cancellationTask);
// In case of cancellation, register a continuation to observe any unhandled exceptions
// from the asynchronous operation (once it completes). In .NET 4.0, unobserved task
// exceptions would terminate the process.
if (readyTask == cancellationTask)
asyncTask.ContinueWith(_ => asyncTask.Exception,
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
await readyTask;
}
但是,基础任务仍然执行完成。这不是一个很大的问题,但有时候底层任务永远不会完成并消耗99%的CPU。
有没有办法杀死&#34;没有杀死进程的任务?
答案 0 :(得分:6)
我正在使用另一个StackOverflow问题的扩展方法
该代码非常陈旧。
现代AsyncEx方法是一种扩展方法Task.WaitAsync
,如下所示:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token;
await myTask.WaitAsync(ct);
我喜欢API如何结束,因为更明确的是等待被取消,而不是操作本身。
有没有办法在不杀死进程的情况下“杀死”任务?
没有
理想的解决方案是与您正在使用的库的作者联系,让他们添加对CancellationToken
的支持。
除此之外,您正处于“取消无法解决的操作”场景,可以通过以下方式解决:
答案 1 :(得分:0)
如您所知,您可以通过传递CancellationToken
然后拨打Cancel
取消任务。
关于如何触发取消取决于应用程序的性质。
一些可能的情况
在案例1中,您只需从取消按钮取消任务,例如
private void cancel_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
await MyAsyncTask(cts.Token);
cts.Cancel();
...
}
在案例2中,您可以在启动任务时启动计时器,然后使用CancelAfter
在设定时间后取消任务,例如
private void start_Click(object sender, RoutedEventArgs e)
{
...
cts = new CancellationTokenSource();
cts.CancelAfter(30000);
await MyAsyncTask(cts.Token);
...
}
在案例3中,您可以对进度做一些事情,例如
private void start_Click(object sender, RoutedEventArgs e)
{
...
Progress<int> progressIndicator = new Progress<int>(ReportProgress);
cts = new CancellationTokenSource();
await MyAsyncTask(progressIndicator, cts.Token);
...
}
void ReportProgress(int value)
{
// Cancel if no progress
}
以下是一些有用的链接 Parallel programming,task cancellation,progress and cancellation,cancel tasks after set time和cancel a list of tasks。
答案 2 :(得分:0)
我能想到的唯一方法是更改TaskScheduler
并自行创建用于任务的线程。这是很多工作。
基本概念是创建自己的TaskScheduler
实现,在分配了自己的调度程序的情况下启动新任务。这样,您就可以使调度程序成为当前调度程序,并从此任务开始执行有问题的任务。
仍然有可能无效的原因。如果导致您麻烦的任务使用默认任务计划程序创建更多任务,您仍然会遇到同样的问题。 (Task.Run does so)
如果他们使用async/await
关键字,您的调度程序将保持活动状态。
现在,在您自己控制的情况下,您可以使用Thread.Abort来终止任何任务。
要了解实施能力,您应该看一下ThreadPoolTaskScheduler
。这是调度程序的默认实现。
正如我所说,这是一项很多工作,但我能想到的唯一办法就是杀掉无法取消的任务。
要使测试运行,如果它甚至可以正常运行,您可能只想实现ThreadPoolTaskScheduler
对TaskCreationOptions.LongRunning
选项的行为。因此为每个任务生成一个新线程。