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

时间:2014-09-23 01:18:58

标签: c# .net asynchronous task-parallel-library httpclient

我的网络请求由此代码处理;

Response = await Client.SendAsync(Message, HttpCompletionOption.ResponseHeadersRead, CToken);

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

return await Response.Content.ReadAsStringAsync();

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

3 个答案:

答案 0 :(得分: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();
}

或者,如果你想在超时而不是仅返回默认值(即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();
}

用法是:

var content = await Response.Content.ReadAsStringAsync().WithTimeout(TimeSpan.FromSeconds(1));

答案 1 :(得分:2)

看看How do I cancel non-cancelable async operations?。如果您只想在后台继续请求时完成await,则可以使用作者的WithCancellation扩展方法。这是从文章中复制的:

public static async Task<T> WithCancellation<T>( 
    this Task<T> task, CancellationToken cancellationToken) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    using(cancellationToken.Register( 
                s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) 
        if (task != await Task.WhenAny(task, tcs.Task)) 
            throw new OperationCanceledException(cancellationToken); 
    return await task; 
}

它基本上将原始任务与接受取消令牌的任务相结合,然后使用Task.WhenAny等待这两项任务。因此,当您取消CancellationToken时,secodn任务会被取消但原始任务会继续。只要你不关心它,你就可以使用这种方法。

你可以像这样使用它:

return await Response.Content.ReadAsStringAsync().WithCancellation(token);

<强>更新

您还可以尝试在取消时处理响应。

token.Register(Reponse.Content.Dispose);
return await Response.Content.ReadAsStringAsync().WithCancellation(token);

现在取消令牌时,Content对象将被处理掉。

答案 2 :(得分:-3)

由于返回任务,您可以Wait执行任务,这基本上等同于指定超时:

// grab the task object
var reader = response.Content.ReadAsStringAsync();

// so you're telling the reader to finish in X milliseconds
var timedOut = reader.Wait(X);  

if (timedOut)
{
    // handle timeouts
}
else
{
    return reader.Result;
}