我正在一个周期内下载许多小文件(每个文件的大小约为1 mb),并且我希望在失去互联网连接的情况下中断此周期。但是httpClient.GetAsync
会挂起,并且如果我在执行Wi-Fi时将其关闭(或拔出电源线),也不会引发异常。如果我使用CancelationToken,它也不会被取消(如果它在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();
}
正在使用行从循环中调用
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异常并且无休止地挂起。我正在寻找一种方法来中止它,如果在有限的时间内没有返回值。
答案 0 :(得分:0)
我不确定您要做什么,但您的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);
}
}
}
}