asp.net 不接受取消令牌的取消方法

cyej8jka  于 2023-04-08  发布在  .NET
关注(0)|答案(1)|浏览(144)

我需要取消一个异步方法,而正在进行中。
我所看到的建议是取消令牌,它可以以不同的方式使用:
1.在任务开始前取消它。
1.注册一个回调方法,该方法应处理我打算取消的方法的取消(https://learn.microsoft.com/en-us/dotnet/standard/threading/how-to-register-callbacks-for-cancellation-requests?redirectedfrom=MSDN
1.将取消令牌传递给方法,然后该方法应检查令牌是否已被取消。
问题是我不能使用这些解决方案中的任何一个。我看到了Thread.Abort选项,但现在似乎不赞成使用它;https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0006,任务本身不提供任何取消方式。
但我理想的解决方案是类似于Abort的东西,或者像使用PowerShell时按Ctrl+C。
我的应用程序的运行方式与PowerShell脚本类似。我有一个表单,在其中输入一些参数,然后基于它们运行一个异步方法。但我可能需要在中间取消这个方法,无论出于什么原因(耗时太长,需要更改参数)。因此,该方法将始终启动,无论是注册回调还是传递取消令牌都会带来类似的输出。我必须在每个角落检查取消令牌的状态,这似乎不是最有效的方式,因为该方法不是简单的循环。
是否有可能简单地削减/中止/取消一个方法/任务?
我已经尝试了类似下面的示例。虽然我可以在方法完成之前返回,但它仍然在后台运行,这是我不允许的。

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;
}
1l5u6lss

1l5u6lss1#

.NET(以及大多数现代语言)中的取消是合作的。这意味着应用程序代码可以 * 请求 * 某些操作取消,并且操作必须 * 响应 * 该取消请求以使取消生效。
对于我所看到的建议是取消令牌,可以以不同的方式使用:
有两种响应取消的方法。一种是使用Register注册一个回调,当请求取消时调用。另一种是定期轮询取消令牌,以查看它是否已被取消(通常最好通过ThrowIfCancellationRequested完成,而不是IsCancellationRequested)。
但我理想的解决方案是类似于Abort的东西,或者像使用PowerShell时按Ctrl+C。
Thread.Abort停止使用的原因以及替代ControlledExecution.Run有关于它的大声警告的原因与大多数现代语言只有合作取消的原因相同:粗暴地中止代码对宿主进程来说是不好的。资源保持打开状态,锁仍然被持有,非托管代码有其自身的问题-一般来说,整个应用程序/进程的状态可能会以难以预测的方式被破坏。
在.NET Framework时代,他们非常努力地解决这些问题:给处理ThreadAbortExceptioncatch块带来奇怪的行为,限制执行区域,试图将代码沙箱化到AppDomains中,等等。
我必须在每个角落检查取消令牌的状态,这似乎不是最有效的方法,因为该方法不是一个简单的循环。
这听起来可能是最好的解决方案。你并不是真的需要它们 * 到处 *;通常几个策略性放置的检查足以及时响应取消。由于您的应用程序听起来像一个管道,因此请考虑每当一个步骤/转换器从其通道/输入中拉出一个项目时进行检查。任何正在处理的项目都已完成;下一个就拉不动了
是否有可能简单地削减/中止/取消一个方法/任务?
不知道
如果你真的不想使用合作取消,有一种方法可以正确地中止代码,但这并不是我所说的“简单”。
注意上面中止代码的所有问题;它们都与以某种模糊或不可预测的方式破坏其应用程序/进程的状态有关。那么,解决方案是将不可取消的代码隔离在单独的进程中。然后,您的应用程序可以启动一个实际的单独进程,并在需要取消代码时终止该进程。不可取消的代码可能最终破坏进程状态,但无论如何,该状态都将被丢弃;不可取消的代码可能最终使资源保持开放,但操作系统即将介入并关闭它们。
这是一种大锤式的方法,但它很有效。我不得不这样做了几次。哦,这正是Powershell处理Ctrl-C的方式。
不过,如果可以的话,我只想在一些关键点上添加一些ThrowIfCancellationRequested调用,这需要一些思考,也许还需要一些试错,但总的来说,这比单独的过程要少得多。

相关问题