C# 我的取消代币怎么了?

C# 我的取消代币怎么了?,c#,.net-core,async-await,cancellation-token,C#,.net Core,Async Await,Cancellation Token,请查看运行在.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 Bad

请查看运行在.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;
我这里的问题是
wait
总是等待很长的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秒钟后,将引发异常,您正在向任务传递取消令牌
令牌
,但您没有在
异步
方法中指定如何处理该令牌


您可能需要添加
token.ThrowIfCancellationRequested(),可能受
标记.IsCancellationRequested
的限制。这样,如果调用了
src.Cancel()
,任务将被取消。

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

正如您所演示的,BadSDK.OperationThatNotReceiveCancellationToken不接受CancellationToken,因此不会基于该token采取任何操作。令牌是否通过超时自动请求取消,或者请求是否在其他事项中发出,这并不重要。简单的事实是,BadSDK.OperationThat notreeCeiveCancellationToken根本没有检查它


在您的Edit1中,CancellationToken被传递给Wait,Wait会监视令牌,并在请求取消时退出。但这并不意味着任务被终止或停止,它只是停止等待它。根据你的意愿,这可能会或可能不会实现你想要的。虽然5秒后返回,但任务仍将继续运行。你甚至可以再等一次。然后可能会终止任务,但在实践中,这可能是一件非常糟糕的事情(请参见)

您使用的
StartNew
过载会导致无休止的混乱。更重要的是,它的类型实际上是
StartNew
(嵌套任务),而不是
StartNew

令牌本身不起任何作用。某些代码必须检查令牌,并抛出异常以退出任务

官方文件遵循这一模式:

    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
但是,如果您正在检查令牌,并且可能会引发异常,那么为什么首先需要传入令牌,然后在闭包中使用相同的令牌

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

当任务实例观察到由引发的OperationCanceledException时 用户代码,它将异常的令牌与其关联的令牌进行比较 (传递给创建任务的API的那个)。如果他们 相同,并且令牌的IsCancellationRequested属性返回 是的,任务将其解释为确认取消和 转换到已取消状态

附言


如果您在.Net核心或4.5+,
Task.Run
是首选工厂方法。

1。您不应该使用
Task.Factory.StartNew
。建议使用
任务。运行
。2.为什么你还要把一个任务包装成另一个任务?3.在第一个示例中,取消令牌仅用于创建任务,这就是为什么如果取消令牌在创建任务后触发取消,则不会发生任何事情。在第二个示例中,您取消了外部任务,但内部任务仍将运行。如果您有一个无法取消的任务,则没有适当的方法中止它。如果您刚刚创建了外部任务以取消内部任务,则这是错误的方法。正确的方法是
wait Task.whenay(BadSDK.Operation…,Task.Delay(5000))如果您的BadSDK任务完成或在5s后(以先到者为准),将完成哪个。但是请记住,BadSDK任务仍将在后台运行,它只是在5s后不再等待。@ckuri 1。消息来源?我从来没有读过这样的东西,实际上读过
Run
是一个在
StartNew
上带有默认参数的包装器。2.因为我希望使用cancel令牌,而
操作不包含TreceiveCancellationToken
显然不接受它。3.如果您实际运行了代码,您将看到在
Task.Wait(token)
处抛出异常,而不是下面的一行。这是我第一次尝试时所期待的行为。4.在现实世界中,令牌将作为参数出现,因此我不知道在它过期之前我有多少时间,这就是为什么这里没有应用任何
技术的原因。1。这是合同上说的。3.我从未说明异常抛出的位置,对于我的观点来说,内部任务仍在运行,而这只会中止外部任务也无关紧要。4.CancellationToken可以通过CancellationToken.Register和TaskCancellationSource包装为任务。只要替换Task.Delay这样的CancellationToken包装器任务,您仍然可以使用WhenAny方法。