繁体   English   中英

C# - 我的 CancellationToken 有什么问题?

[英]C# - Whats wrong with my CancellationToken?

请看这段代码,在.Net Core 2.0 上运行:

var src = new CancellationTokenSource(5000);
var token = src.Token;
var responseTask = await Task.Factory.StartNew(async () =>
{
   //Uncomment bellow to reproduce locally
   //await Task.Delay(60000);

   return await BadSDK.OperationThatDoesNotReceiveCancellationToken();//takes around 1 min
}, token);

var response = await responseTask;

我的问题是await总是在等待很长的 sdk 调用,而不是等待 5 秒。

我究竟做错了什么? 我的理解哪里错了?

Edit1:此代码按预期运行:

var src = new CancellationTokenSource(5000);
var token = src.Token;
var responseTask = Task.Factory.StartNew(() =>
{
    var task = BadSDK.OperationThatDoesNotReceiveCancellationToken();
    task.Wait(token);
    cancellationToken.ThrowIfCancellationRequested();
    return task.Result;
}, token);

意思是,5秒后,抛出异常

问题是取消令牌模式期望任务检查令牌并在令牌过期时退出或抛出错误。 编写良好的任务将定期检查取消是否被取消,然后该任务可以进行任何必要的清理并优雅地返回或抛出错误。

正如您所演示的,BadSDK.OperationThatDoesNotReceiveCancellationToken 不接受 CancellationToken,因此不会根据令牌采取任何行动。 令牌是否通过超时自动请求取消,或者请求是否以其他方式发出并不重要。 一个简单的事实是 BadSDK.OperationThatDoesNotReceiveCancellationToken 根本没有检查它。

在您的 Edit1 中,将 CancellationToken 传递给 Wait ,它会监视令牌并在请求取消时退出。 然而,这并不意味着该任务被杀死或停止,它只是停止等待它。 根据您的意图,这可能会或可能不会做您想要的。 虽然它确实在 5 秒后返回,但任务仍将继续运行。 你甚至可以再等一次。 然后可能会终止任务,但在实践中,这可能是一件非常糟糕的事情(请参阅Is it possible to abort a Task like aborting a Thread (Thread.Abort method)?

您使用的StartNew重载是无尽混乱的根源。 加倍如此,因为它的类型实际上是StartNew<Task<T>> (嵌套任务)而不是StartNew<T>

令牌本身什么也不做。 某处的某些代码必须检查令牌,并抛出异常以退出任务。

官方文档遵循这种模式:

    var tokenSource = new CancellationTokenSource();
    var ct = tokenSource.Token;

    var task = Task.Run(() =>
    {
        while (...)
        {
            if (ct.IsCancellationRequested) 
            {
                // cleanup your resources before throwing

                ct.ThrowIfCancellationRequested();
            }
        }
    }, ct); // Pass same token to Task.Run

但是,如果您无论如何都要检查令牌,并且可能会抛出异常,为什么您需要首先传入令牌,然后在闭包中使用相同的令牌?

原因是您传入的令牌用于将任务移动到已取消的 state。

当任务实例观察到用户代码抛出的 OperationCanceledException 时,它会将异常的标记与其关联的标记(传递给创建任务的 API 的标记)进行比较。 如果它们相同并且令牌的 IsCancellationRequested 属性返回 true,则任务将此解释为确认取消并转换到 Canceled state。

附言

如果您使用的是 .Net Core 或 4.5+, Task.Run优于工厂方法。

您正在向任务传递取消令牌token ,但您没有在async方法中指定如何处理该令牌。

您可能想要添加token.ThrowIfCancellationRequested(); 在方法中,可能以token.IsCancellationRequested为条件。 这样,如果src.Cancel() ,任务将被取消。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM