在没有 CancellationToken 的情况下停止任务
Stopping a task without a CancellationToken
我正在使用具有 async
方法但没有 CancellationToken
重载的外部库。
现在我正在使用另一个 Whosebug 问题的扩展方法来添加 CancellationToken
:
public async static Task HandleCancellation(this Task asyncTask, CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested.
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.
有什么方法可以"kill"任务而不终止进程吗?
正如您所建议的,您可以通过传入 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, and cancel a list of tasks.
我能想到的唯一方法是更改 TaskScheduler
并自己管理用于任务的线程的创建。这是很多工作。
基本概念是创建您自己的 TaskScheduler
实现,使用您自己分配的调度程序启动新任务。通过这种方式,您可以让您的调度程序成为当前调度程序,并从此任务开始有问题的任务。
还有一些原因可能不起作用。如果给您带来麻烦的任务使用默认任务计划程序创建了更多任务,您仍然会遇到同样的问题。 (Task.Run does so)
如果他们使用 async/await
关键字,您的调度程序将保持活动状态。
现在有了您自己控制的调度程序,您可以使用 Thread.Abort.
终止任何任务
要了解有关实施的信息,您应该查看 ThreadPoolTaskScheduler
。这是调度程序的默认实现。
正如我所说,这是很多工作,但我能想到的唯一方法是终止无法取消的任务。
要获得测试 运行 如果它甚至有效,您可能只想实现 ThreadPoolTaskScheduler
对 TaskCreationOptions.LongRunning
选项的行为。因此为每个任务生成一个新线程。
I am using an extension method from another Whosebug question
那个代码很旧。
现代 AsyncEx 方法是一种扩展方法 Task.WaitAsync
,如下所示:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token;
await myTask.WaitAsync(ct);
我喜欢 API 的结局,因为更清楚的是 wait 被取消,而不是操作本身。
Is there any way to "kill" the task without killing the process?
没有
理想的解决方案是联系您正在使用的库的作者,让他们添加对 CancellationToken
.
的支持
除此之外,您处于"cancel an uncancelable operation"场景,可以通过以下方式解决:
- 将代码放在一个单独的进程中,并在取消时终止该进程。这是唯一完全安全但最困难的解决方案。
- 将代码放在单独的应用程序域中,并在取消时卸载该应用程序域。这并不完全安全;终止的应用程序域可能导致进程级资源泄漏。
- 将代码放在一个单独的线程中,并在取消时终止该线程。这更不安全;终止的线程会破坏程序内存。
我正在使用具有 async
方法但没有 CancellationToken
重载的外部库。
现在我正在使用另一个 Whosebug 问题的扩展方法来添加 CancellationToken
:
public async static Task HandleCancellation(this Task asyncTask, CancellationToken cancellationToken)
{
// Create another task that completes as soon as cancellation is requested.
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.
有什么方法可以"kill"任务而不终止进程吗?
正如您所建议的,您可以通过传入 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, and cancel a list of tasks.
我能想到的唯一方法是更改 TaskScheduler
并自己管理用于任务的线程的创建。这是很多工作。
基本概念是创建您自己的 TaskScheduler
实现,使用您自己分配的调度程序启动新任务。通过这种方式,您可以让您的调度程序成为当前调度程序,并从此任务开始有问题的任务。
还有一些原因可能不起作用。如果给您带来麻烦的任务使用默认任务计划程序创建了更多任务,您仍然会遇到同样的问题。 (Task.Run does so)
如果他们使用 async/await
关键字,您的调度程序将保持活动状态。
现在有了您自己控制的调度程序,您可以使用 Thread.Abort.
终止任何任务要了解有关实施的信息,您应该查看 ThreadPoolTaskScheduler
。这是调度程序的默认实现。
正如我所说,这是很多工作,但我能想到的唯一方法是终止无法取消的任务。
要获得测试 运行 如果它甚至有效,您可能只想实现 ThreadPoolTaskScheduler
对 TaskCreationOptions.LongRunning
选项的行为。因此为每个任务生成一个新线程。
I am using an extension method from another Whosebug question
那个代码很旧。
现代 AsyncEx 方法是一种扩展方法 Task.WaitAsync
,如下所示:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token;
await myTask.WaitAsync(ct);
我喜欢 API 的结局,因为更清楚的是 wait 被取消,而不是操作本身。
Is there any way to "kill" the task without killing the process?
没有
理想的解决方案是联系您正在使用的库的作者,让他们添加对 CancellationToken
.
除此之外,您处于"cancel an uncancelable operation"场景,可以通过以下方式解决:
- 将代码放在一个单独的进程中,并在取消时终止该进程。这是唯一完全安全但最困难的解决方案。
- 将代码放在单独的应用程序域中,并在取消时卸载该应用程序域。这并不完全安全;终止的应用程序域可能导致进程级资源泄漏。
- 将代码放在一个单独的线程中,并在取消时终止该线程。这更不安全;终止的线程会破坏程序内存。