HttpClient.GetAsync 挂起,然后互联网在其执行过程中消失
Posted
技术标签:
【中文标题】HttpClient.GetAsync 挂起,然后互联网在其执行过程中消失【英文标题】:HttpClient.GetAsync hangs then internet disappears during its execution 【发布时间】:2019-06-10 20:25:06 【问题描述】:我在一个循环中下载了许多小文件(每个大小约为 1 mb),我希望这个循环在互联网连接丢失的情况下中断。但是httpClient.GetAsync
挂起并且如果我在执行时关闭 wi-fi(或拔掉电源线)不会抛出异常。
如果我使用CancellationToken
,它也不会被取消(如果它在httpClient.GetAsync
之前被取消,它会被取消,但我需要在执行期间取消它)。
我的经验表明,如果在调用httpClient.GetAsync(url, cancellationToken);
之前互联网消失了,它会抛出异常,但是如果互联网消失然后httpClient.GetAsync(url, cancellationToken);
已经开始执行,那么它将无休止地挂起,即使我触发设置CancelationTokenSource.Cancel()
操作不会取消。
带有HttpClient
的函数用法:
private static readonly HttpClient httpClient = new HttpClient();
protected async Task<byte[]> HttpGetData(string url, CancellationToken cancellationToken)
var response = await httpClient.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
正在使用 line 从循环中调用它
byte[] data = await LimitedTimeAwaiter<byte[]>.Execute(
async (c) => return await HttpGetData(chunkUrl, c); , cancellationToken, 5);
这里是LimitedTimeAwaiter
代码
public class LimitedTimeAwaiter<T>
public static async Task<T> Execute(Func<CancellationToken, Task<T>> function, CancellationToken originalToken, int awaitTime)
originalToken.ThrowIfCancellationRequested();
CancellationTokenSource timeout = new CancellationTokenSource(TimeSpan.FromSeconds(awaitTime));
try
return await function(timeout.Token);
catch (OperationCanceledException err)
throw new Exception("LimitedTimeAwaiter ended function ahead of time", err);
虽然我取消了给httpClient.GetAsync
的令牌,但不会抛出OperationCanceledException
并无休止地挂起。如果在有限的时间内没有返回值,我正在寻找一种中止它的方法。
【问题讨论】:
HttpClient
不应该在 60 秒后超时吗?
也许应该,但没有,我等了大约 10 分钟没有结果。
【参考方案1】:
我不能 100% 确定您要做什么,但您的 LimitedTimeAwaiter 存在缺陷。您实际上并未将 originalToken
提供给 HttpClient,因此它不会用于取消。要解决这个问题,您应该链接您的两个令牌:
public class LimitedTimeAwaiter<T>
public static async Task<T> Execute(Func<CancellationToken, Task<T>> function, CancellationToken originalToken, int awaitTime)
originalToken.ThrowIfCancellationRequested();
var timeout = CancellationTokenSource.CreateLinkedTokenSource(originalToken);
timeout.CancelAfter(TimeSpan.FromSeconds(awaitTime));
try
return await function(timeout.Token);
catch (OperationCanceledException err)
throw new Exception("LimitedTimeAwaiter ended function ahead of time", err);
(也不完全确定为什么会捕获异常,但这不是重点)。
现在我将假设您的问题实际上是即使您取消了正确的令牌,HttpClient 返回的任务也没有完成。首先,您应该报告它,因为这将是 HttpClient 实现中的错误。然后您可以使用此解决方法:
public class LimitedTimeAwaiter<T>
public static async Task<T> Execute(Func<CancellationToken, Task<T>> function, CancellationToken originalToken, int awaitTime)
originalToken.ThrowIfCancellationRequested();
using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(originalToken))
timeout.CancelAfter(TimeSpan.FromSeconds(awaitTime));
try
var httpClientTask = function(timeout.Token);
var timeoutTask = Task.Delay(Timeout.Infinite, timeout.Token); // This is a trick to link a task to a CancellationToken
var task = await Task.WhenAny(httpClientTask, timeoutTask);
// At this point, one of the task completed
// First, check if we timed out
timeout.Token.ThrowIfCancellationRequested();
// If we're still there, it means that the call to HttpClient completed
return await httpClientTask;
catch (OperationCanceledException err)
throw new Exception("LimitedTimeAwaiter ended function ahead of time", err);
【讨论】:
清理相关的计时器我认为这一切都可以通过将timeout
包装在using
块中来避免...CancellationTokenSource
是IDisposable
@987654321 @.
1) 我捕获异常是有原因的,originalToken
用于将下载状态从“正在下载”设置为“已暂停”,并用于对用户的操作做出反应。而如果我们无法下载文件,则下载状态应变为“错误”。所以 'originalToken' 不应与 'timeout' 相关联并传递给 HttpClient(取消后我们可以再下载一个文件即可)。 2)这个解决方案看起来不错,我稍后会检查它。现在我用HttpWebRequest.Create(url)\HttpWebResponse.GetResponseAsync\response.GetResponseStream.ReadAsync
替换了HttpClient,它被超时令牌取消了。以上是关于HttpClient.GetAsync 挂起,然后互联网在其执行过程中消失的主要内容,如果未能解决你的问题,请参考以下文章
连接到 *** 时 HttpClient.GetAsync 超时
HttpClient.GetAsync,无效的 URI 异常
使用 HttpClient.GetAsync() 时如何确定 404 响应状态
HttpClient.GetAsync 一次只执行 2 个请求?
在 Azure Function 上获取数据时,HttpClient.GetAsync() 会产生 AggregateException