在OperationCancelledException的情况下,我正在尝试确保可取消代码的这些语义:
如果呼叫者是原因,请尊重并重新抛出
如果是我们或任何下游代码的原因,请将其翻译为 “不可用”响应
不幸的是,第二部分有问题。
如果下游代码链接了一个源,然后取消了+抛出,我似乎无法检测到它。我希望自己的消息源会引发IsCancellationRequested,但不会。
下面是一些说明问题的代码
public void TestDownstreamCancellation()
{
var cancellationToken = new CancellationTokenSource().Token;
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
try
{
var inner = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
inner.Cancel();
inner.Token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException e)
{
// All right - we may be seeing this because we have cancelled the internal tasks (they execute
// cancellationToken.ThrowIfCancellationRequested(), which is a rational way to bail out).
// But it could also be because the outer (caller owned) token cancelled, in which case we should re-throw.
if (e.CancellationToken == cancellationToken)
{
// The cause of the exception is that the caller of the composite requested termination, re-throwing!
// Case is separated out for clatiry and (possibly) logging purposes.
throw;
}
else if (cts.Token.IsCancellationRequested)
{
// Check if the cause is actually the passed-in token. If so, we should not translate this exception to a
// ServiceUnavailable response. May be redundant, as the previous if branch should have caught this
cancellationToken.ThrowIfCancellationRequested();
// The cause of the exception is that one of the sub-tasks responded to a cancellation request by throwing
// OperationCanceled. We translate this to ServiceUnavailable. It's always iffy to rely on exception behavior for control
// flow, but in this case it's quite well-defined what's going on, as it's very common for an async task to honor a
// cancellation request by throwing this exception.
// return ServiceUnavailable;
}
else
{
// Something else threw, so raise it again and let the caller deal with it.
throw;
}
}
我希望第二。分支被触发,但不会触发-相反,我们点击了第三个分支并重新抛出。