为什么 OperationCancelledException 传送的 CancellationToken 与来自 CTSource 的不同?
Why is OperationCancelledException conveying different CancellationToken than the one from CTSource?
为什么异常中保存的CancellationToken和CancellationTokenSource提供的token不一样?
[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);
}
}
Why is the CancellationToken stored in the exception different to token provided by CancellationTokenSource?
这是一个实现细节。我没有查看源代码,但我 怀疑 正在发生的是提供给 ExecuteScalarAsync
的 CancellationToken
与一些内部 CancellationToken
这意味着 "we lost the connection" 或类似的东西。这些 linked CancellationToken
s 不等同于它们的来源 CancellationToken
。
这通常是 CancellationToken
的使用问题。对于链接令牌,并不总是能够确定 哪个 取消令牌导致了取消。出于这个原因,我建议通过 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);
}
可能会出现竞争情况,您的代码会 认为 取消是由于超时而发生的,但实际上是因为连接断开(或任何内部逻辑正在发生) on), 但在大多数情况下这无关紧要。
为什么异常中保存的CancellationToken和CancellationTokenSource提供的token不一样?
[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);
}
}
Why is the CancellationToken stored in the exception different to token provided by CancellationTokenSource?
这是一个实现细节。我没有查看源代码,但我 怀疑 正在发生的是提供给 ExecuteScalarAsync
的 CancellationToken
与一些内部 CancellationToken
这意味着 "we lost the connection" 或类似的东西。这些 linked CancellationToken
s 不等同于它们的来源 CancellationToken
。
这通常是 CancellationToken
的使用问题。对于链接令牌,并不总是能够确定 哪个 取消令牌导致了取消。出于这个原因,我建议通过 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);
}
可能会出现竞争情况,您的代码会 认为 取消是由于超时而发生的,但实际上是因为连接断开(或任何内部逻辑正在发生) on), 但在大多数情况下这无关紧要。