如何为不接受取消令牌的异步功能设置超时?

igu*_*man 7 .net c# asynchronous httpclient task-parallel-library

我有这个代码处理的Web请求;

Response = await Client.SendAsync(Message, HttpCompletionOption.ResponseHeadersRead, CToken);
Run Code Online (Sandbox Code Playgroud)

在读取响应头之后和内容完成读取之前返回.当我打电话给这一行来获取内容时......

return await Response.Content.ReadAsStringAsync();
Run Code Online (Sandbox Code Playgroud)

我希望能在X秒后停止它.但它不接受取消令牌.

i3a*_*non 10

虽然您可以依赖于WithCancellation重用目的,但是更简单的超时解决方案(不抛出OperationCanceledException)将是创建超时任务Task.Delay并等待第一个任务完成,使用Task.WhenAny:

public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
    var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
    return Task.WhenAny(task, timeoutTask).Unwrap();
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您想要在超时而不是仅返回默认值(即null)时抛出异常:

public static async Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
    if (task == await Task.WhenAny(task, Task.Delay(timeout)))
    {
        return await task;
    }
    throw new TimeoutException();
}
Run Code Online (Sandbox Code Playgroud)

用法是:

var content = await Response.Content.ReadAsStringAsync().WithTimeout(TimeSpan.FromSeconds(1));
Run Code Online (Sandbox Code Playgroud)

  • 虽然NedStoyanov的答案也很棒,但这个答案特别提供了处理超时的代码,而不仅仅是允许取消.我觉得这个更好地回答了这个问题. (3认同)