为什么OperationCancelledException传递的CancellationToken与CTSource的不同?

时间:2019-04-19 12:53:42

标签: c# async-await ado.net

为什么在CancellationToken中存储的CancellationToken与CancellationTokenSource提供的令牌不同?

[Test]
public static async Task SqlCommand_should_recognise_which_CT_triggered_its_cancellation()
{

    var timeout = TimeSpan.FromSeconds(1);
    var cts = new CancellationTokenSource(timeout);

    try
    {
        var connection = new SqlConnection(_config.ConnectionString);
        await connection.OpenAsync(cts.Token);
        var sqlQuery = new SqlCommand("select 1", connection);

        await Task.Delay(timeout + TimeSpan.FromSeconds(1));
        await sqlQuery.ExecuteScalarAsync(cts.Token);
    }
    catch (OperationCanceledException cancelledEx)
    {
        //Shouldn't they be the same?
        Assert.AreEqual(cancelledEx.CancellationToken, cts.Token);
        // The below fails as well
        // Assert.IsTrue(cancelledEx.CancellationToken == cts.Token);


    }
}

1 个答案:

答案 0 :(得分:2)

  

为什么在CancellationToken中存储的CancellationToken与CancellationTokenSource提供的令牌不同?

这是一个实现细节。我没有查看来源,但我可疑的情况是,提供给CancellationToken的{​​{1}}与一些内部ExecuteScalarAsync组合在一起,这意味着“我们失去了联系”或类似的东西。这些linked CancellationTokens不等同于其来源CancellationToken

这通常是CancellationToken的使用问题。使用链接令牌,并非总是能够确定哪个 取消令牌导致了取消。因此,建议您通过CancellationToken检查取消令牌的自己副本:

catch (OperationCanceledException ex) when (cts.IsCancellationRequested)

在竞争情况下,您的代码可能会认为发生取消是因为超时,但实际上是由于连接断开(或发生任何内部逻辑)导致的,在大多数情况下,这都没有关系。