我需要在进程中间取消异步方法。
据我所见,建议是
CancelationToken
,它可以以不同的方式使用:
问题是我无法使用这些解决方案中的任何一个。我看到了
Thread.Abort
的选项,但现在似乎已弃用; SYSLIB0006:不支持Thread.Abort。并且Task本身不提供任何取消方法。
但我理想的解决方案类似于
Abort
。或者就像使用 PowerShell 时我们按 Ctrl+C。
我的应用程序的运行方式与 PowerShell 脚本类似。我有一个表单,我在其中输入一些参数,然后基于它们运行异步方法。但我可能出于某种原因需要在中间取消这个方法(花费太长时间,需要更改参数)。 因此,该方法将始终启动,并且注册回调或传递
CancelationToken
都会带来类似的输出。我必须在每个角落检查取消令牌状态来填充该方法,这似乎不是最有效的方法,因为该方法不是一个简单的循环。
是否有可能简单地剪切/中止/取消方法/任务?
我尝试过类似下面的示例。虽然我可以在方法完成之前返回,但这仍然在后台运行,这是我不能允许的。
private static async Task<decimal> LongRunningOperationWithCancellationTokenAsync(
int loop, CancellationToken cancellationToken)
{
// We create a TaskCompletionSource of decimal
var taskCompletionSource = new TaskCompletionSource<decimal>();
// Registering a lambda into the cancellationToken
cancellationToken.Register(() =>
{
// We received a cancellation message, cancel the TaskCompletionSource.Task
taskCompletionSource.TrySetCanceled();
});
var task = LongRunningOperation(loop);
// Wait for the first task to finish among the two
var completedTask = await Task.WhenAny(task, taskCompletionSource.Task);
// If the completed task is our long running operation we set its result.
if (completedTask == task)
{
// Extract the result
// The task is finished and the await will return immediately
var result = await task;
// Set the taskCompletionSource result
taskCompletionSource.TrySetResult(result);
}
// Return the result of the TaskCompletionSource.Task
return await taskCompletionSource.Task;
}
.NET(以及大多数现代语言)中的取消是合作性的。这意味着应用程序代码可以请求取消某些操作,并且该操作必须响应该取消请求才能使取消生效。
据我所知,建议是取消令牌,它可以以不同的方式使用:
响应取消的一般方法有两种。一种是使用
Register
注册一个回调,在请求取消时调用该回调。另一种方法是定期轮询取消令牌以查看它是否已被取消(通常最好通过 ThrowIfCancellationRequested
完成,而不是 IsCancellationRequested
)。
但我理想的解决方案是类似于 Abort 的东西。或者就像使用 PowerShell 时我们按 Ctrl+C。
Thread.Abort
被终止以及替代品 ControlledExecution.Run
有响亮的警告的原因与大多数现代语言仅具有合作取消的原因相同:粗鲁地中止代码对托管过程不利。资源保持打开状态,锁定保持不变,非托管代码有其自身的问题 - 一般来说,整个应用程序/进程的状态可能会以难以预测的方式损坏。
在 .NET Framework 时代,他们非常努力地解决这些问题:给
catch
块提供奇怪的行为,处理 ThreadAbortException
、限制执行区域、尝试将代码沙箱到 AppDomains 中,等等。但最终这一切,粗鲁中止的方法实在是太不可预测了。
我必须在方法中填充每个角落的取消令牌状态检查,这似乎不是最有效的方法,因为该方法不是一个简单的循环。
这听起来可能是最好的解决方案。你并不真的需要它们到处;通常,一些策略性的检查就足以及时响应取消的情况。由于您的应用程序听起来像管道,因此请考虑在步骤/转换器从其通道/输入中提取项目时进行检查。任何已处理的项目均已完成;它只是不会拉下一个。
是否有可能简单地剪切/中止/取消方法/任务?
没有。
如果您确实不想使用协作取消,那么有可以正确中止代码。但这不是我所说的“简单”。
注意上面中止代码的所有问题;它们都与以某种模糊或不可预测的方式破坏应用程序/进程的状态有关。那么,解决方案就是将不可取消的代码隔离在单独的进程中。然后,您的应用程序可以启动一个实际的单独进程,并在需要中止取消代码时终止它。不可取消的代码可能最终会破坏进程状态,但无论如何该状态都会被丢弃;不可取消的代码可能最终会使资源保持打开状态,但操作系统无论如何都会介入并关闭它们。
这是大锤式的方法,但它确实有效。我不得不这样做几次。哦,这正是 Powershell 处理 Ctrl-C 的方式。
不过,如果可以的话,我只是在某些战略点添加一些
ThrowIfCancellationRequested
调用。这需要一些思考,也许还需要一些尝试和错误,但总体而言,它比单独的过程要少得多。