Ben*_*all 11 c# http cancellation cancellationtokensource dotnet-httpclient
我有一些代码通过调用许多其他服务来验证某些数据.我并行启动所有调用,然后等到其中至少一个完成.如果任何请求失败,我不关心其他调用的结果.
我打电话,HttpClient我已经通过了HttpMessageHandler一个记录.实质上:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
try
{
response = await base.SendAsync(request, cancellationToken);
}
catch (OperationCanceledException ex)
{
LogTimeout(...);
throw;
}
catch (Exception ex)
{
LogFailure(...);
throw;
}
finally
{
LogComplete(...);
}
return response;
}
Run Code Online (Sandbox Code Playgroud)
当我取消请求时,我遇到麻烦的部分没有.当我取消请求时,我是故意这样做的,所以我不希望它被记录为超时,但是在取消和实际超时之间似乎没有任何区别.
反正有没有完成这个?
编辑: 我需要澄清一下这一点.并行调用的服务是使用超时传递CancellationTokens:
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(2));
Run Code Online (Sandbox Code Playgroud)
因此,当服务器响应时间超过两秒时,我得到一个OperationCanceledException,如果我手动取消令牌源(比如因为另一台服务器在1秒后返回错误),那么我仍然得到一个OperationCanceledException.理想情况下,我将能够看到CancellationToken.IsCancellationRequested以确定它是否被取消,由于超时,而不是明确要求被取消,但现在看来,你得到相同的值,而不管怎么就被取消了.
如果要区分两种取消类型,则需要使用两种不同的取消令牌.别无他法.这不是太难,因为它们可以联系起来 - 只是有点尴尬.
编写此IMO的最简单方法是将超时代码移动到SendAsync方法而不是调用方法:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
cts.CancelAfter(TimeSpan.FromSeconds(2));
try
{
return await base.SendAsync(request, cancellationToken);
}
catch (OperationCanceledException ex)
{
if (cancellationToken.IsCancellationRequested)
return null;
LogTimeout(...);
throw;
}
catch (Exception ex)
{
LogFailure(...);
throw;
}
finally
{
LogComplete(...);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您不想将超时代码移入SendAsync,那么您还需要在该方法之外进行日志记录.
如果异常没有告诉您两种情况之间的区别,那么您将需要检查 theTask或 theCancellationToken以查看是否确实有取消。
如果抛出未处理的(最有可能在内部使用),我会倾向于询问Task哪个将使其IsCanceled属性返回 true 。像这样的东西...OperationCanceledExceptionCancellationToken.ThrowIfCancellationRequestedbase.SendAsync
HttpResponseMessage response = null;
Task sendTask = null;
try
{
sendTask = base.SendAsync(request, cancellationToken);
await sendTask;
}
catch (OperationCanceledException ex)
{
if (!sendTask.IsCancelled)
{
LogTimeout(...);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
针对问题的更新,我想更新我的答案。你是对的,无论是特别要求取消还是CancellationTokenSource由超时引起,都会导致完全相同的结果。如果你反编译,CancellationTokenSource你会看到超时它只是设置一个Timer回调,CancellationTokenSource.Cancel当达到超时时会显式调用,所以两种方式最终都会调用相同的Cancel方法。
我想如果你想区分你需要从CancellationTokenSource(它不是一个sealed类)派生,然后添加你自己的自定义取消方法,该方法将设置一个标志,让你知道你明确取消了操作而不是让它暂停。
这是不幸的,因为您将同时拥有自定义取消方法和原始Cancel方法,并且必须确保使用自定义方法。您可以通过使用以下内容隐藏现有Cancel操作来摆脱自定义逻辑:
class CustomCancellationTokenSource : CancellationTokenSource
{
public bool WasManuallyCancelled {get; private set;}
public new void Cancel()
{
WasManuallyCancelled = true;
base.Cancel();
}
}
Run Code Online (Sandbox Code Playgroud)
我认为隐藏基本方法会起作用,您可以试一试并找出答案。
| 归档时间: |
|
| 查看次数: |
3307 次 |
| 最近记录: |