不接受取消令牌的取消方法

问题描述 投票:0回答:1

我需要在进程中间取消异步方法。

据我所见,建议是

CancelationToken
,它可以以不同的方式使用:

  1. 在任务开始之前取消任务。
  2. 注册一个回调方法,该方法应该处理我打算取消的方法的取消(注册取消请求的回调)。
  3. 将取消令牌传递给方法,然后该方法应该检查令牌是否已被取消。

问题是我无法使用这些解决方案中的任何一个。我看到了

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;
}
c# asp.net asynchronous async-await cancellation-token
1个回答
8
投票

.NET(以及大多数现代语言)中的取消是合作性的。这意味着应用程序代码可以请求取消某些操作,并且该操作必须响应该取消请求才能使取消生效。

据我所知,建议是取消令牌,它可以以不同的方式使用:

响应取消的一般方法有两种。一种是使用

Register
注册一个回调,在请求取消时调用该回调。另一种方法是定期轮询取消令牌以查看它是否已被取消(通常最好通过
ThrowIfCancellationRequested
完成,而不是
IsCancellationRequested
)。

但我理想的解决方案是类似于 Abort 的东西。或者就像使用 PowerShell 时我们按 Ctrl+C。

Thread.Abort
被终止以及替代品
ControlledExecution.Run
有响亮的警告的原因与大多数现代语言仅具有合作取消的原因相同:粗鲁地中止代码对托管过程不利。资源保持打开状态,锁定保持不变,非托管代码有其自身的问题 - 一般来说,整个应用程序/进程的状态可能会以难以预测的方式损坏。

在 .NET Framework 时代,他们非常努力地解决这些问题:给

catch
块提供奇怪的行为,处理
ThreadAbortException
、限制执行区域、尝试将代码沙箱到 AppDomains 中,等等。但最终这一切,粗鲁中止的方法实在是太不可预测了。

我必须在方法中填充每个角落的取消令牌状态检查,这似乎不是最有效的方法,因为该方法不是一个简单的循环。

这听起来可能是最好的解决方案。你并不真的需要它们到处;通常,一些策略性的检查就足以及时响应取消的情况。由于您的应用程序听起来像管道,因此请考虑在步骤/转换器从其通道/输入中提取项目时进行检查。任何已处理的项目均已完成;它只是不会拉下一个。

是否有可能简单地剪切/中止/取消方法/任务?

没有。

如果您确实不想使用协作取消,那么可以正确中止代码。但这不是我所说的“简单”。

注意上面中止代码的所有问题;它们都与以某种模糊或不可预测的方式破坏应用程序/进程的状态有关。那么,解决方案就是将不可取消的代码隔离在单独的进程中。然后,您的应用程序可以启动一个实际的单独进程,并在需要中止取消代码时终止它。不可取消的代码可能最终会破坏进程状态,但无论如何该状态都会被丢弃;不可取消的代码可能最终会使资源保持打开状态,但操作系统无论如何都会介入并关闭它们。

这是大锤式的方法,但它确实有效。我不得不这样做几次。哦,这正是 Powershell 处理 Ctrl-C 的方式。

不过,如果可以的话,我只是在某些战略点添加一些

ThrowIfCancellationRequested
调用。这需要一些思考,也许还需要一些尝试和错误,但总体而言,它比单独的过程要少得多。

© www.soinside.com 2019 - 2024. All rights reserved.