为什么在异常中存储的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);
}
}
为什么在异常中存储的CancellationToken与CancellationTokenSource提供的令牌不同?
这是一个实现细节。我没有看过消息来源,但我怀疑正在发生的事情是提供给CancellationToken
的ExecuteScalarAsync
正在与一些内部CancellationToken
相结合,这意味着“我们失去了联系”或类似的东西。这些linked CancellationToken
s不等同于他们的来源CancellationToken
s。
这是CancellationToken
s的一般使用问题。使用链接的令牌,并不总是可以确定哪个取消令牌导致取消。因此,我建议您通过catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
检查自己的取消令牌副本:
static async Task Main(string[] args)
{
var timeout = TimeSpan.FromSeconds(1);
var cts = new CancellationTokenSource(timeout);
try
{
await IndirectDelay(10, cts.Token);
await Task.Delay(timeout + TimeSpan.FromSeconds(1));
await IndirectDelay(10, cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
Console.WriteLine(ex.CancellationToken == cts.Token); // false
Console.WriteLine("Our token is canceled.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Canceled for some other reason.");
}
catch (Exception)
{
Console.WriteLine("General error.");
}
}
private static async Task IndirectDelay(int timeout, CancellationToken token)
{
using (var internalCts = new CancellationTokenSource())
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token, internalCts.Token))
await Task.Delay(timeout, cts.Token);
}
存在竞争条件的可能性,您的代码会认为由于超时而发生取消,但实际上是因为连接丢失(或者内部逻辑正在发生),但在大多数情况下这并不重要。