C# HttpClient.GetAsync挂起,然后internet在其执行过程中消失

C# HttpClient.GetAsync挂起,然后internet在其执行过程中消失,c#,http,.net-core,async-await,cancellationtokensource,C#,Http,.net Core,Async Await,Cancellationtokensource,我在一个周期内下载了很多小文件(每个文件的大小约为1MB),我希望这个周期能够中断,以防失去互联网连接。但是httpClient.GetAsync会挂起,如果我在执行wi-fi时关闭wi-fi(或拔出连接线),则不会出现异常。另外,如果我使用CancelationToken,它不会被取消(如果它在httpClient.GetAsync之前被取消,它会被取消,但我需要在执行过程中取消它) 我的经验表明,如果internet在httpClient.GetAsync(url,cancellationT

我在一个周期内下载了很多小文件(每个文件的大小约为1MB),我希望这个周期能够中断,以防失去互联网连接。但是
httpClient.GetAsync
会挂起,如果我在执行wi-fi时关闭wi-fi(或拔出连接线),则不会出现异常。另外,如果我使用CancelationToken,它不会被取消(如果它在
httpClient.GetAsync
之前被取消,它会被取消,但我需要在执行过程中取消它)

我的经验表明,如果internet在
httpClient.GetAsync(url,cancellationToken)之前消失,会发生什么时,它将抛出一个异常,但如果internet消失,则
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();
        }
private static readonly HttpClient HttpClient=new HttpClient();
受保护的异步任务HttpGetData(字符串url、CancellationToken CancellationToken)
{
var response=wait httpClient.GetAsync(url,cancellationToken);
response.EnsureSuccessStatusCode();
return wait response.Content.ReadAsByteArrayAsync();
}
正在使用行从循环调用

byte[] data = await LimitedTimeAwaiter<byte[]>.Execute(
async (c) => { return await HttpGetData(chunkUrl, c); }, cancellationToken, 5);

byte[]data=wait limitedtimewaiter.Execute(
async(c)=>{return await-HttpGetData(chunkUrl,c);},cancellationToken,5);
这是LimitedTimeWaiter代码

    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);
            }
        }
    }
public class limitedtimewaiter
{
公共静态异步任务执行(Func函数、CancellationToken originalToken、int-awaitTime)
{
originalToken.ThrowIfCancellationRequested();
CancellationTokenSource超时=新的CancellationTokenSource(TimeSpan.FromSeconds(WaitTime));
尝试
{
返回等待函数(timeout.Token);
}
捕获(操作取消异常错误)
{
抛出新异常(“LimitedTimeWaiter提前结束函数”,err);
}
}
}

当我取消给予httpClient.GetAsync的令牌时,它不会抛出OperationCanceledException并无限挂起。如果在有限的时间内没有返回值,我正在寻找一种方法来中止它。

我不是100%确定您要做什么,但您的LimitedTimeWaitier中有一个缺陷。实际上,您没有将
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);
        }
    }
}
public class limitedtimewaiter
{
公共静态异步任务执行(Func函数、CancellationToken originalToken、int-awaitTime)
{
originalToken.ThrowIfCancellationRequested();
var timeout=CancellationTokenSource.CreateLinkedTokenSource(originalToken);
timeout.CancelAfter(TimeSpan.FromSeconds(waittime));
尝试
{
返回等待函数(timeout.Token);
}
捕获(操作取消异常错误)
{
抛出新异常(“LimitedTimeWaiter提前结束函数”,err);
}
}
}
(也不完全确定为什么会出现异常,但这与问题无关)

现在我假设您的问题是,即使您取消了正确的令牌,HttpClient返回的任务也没有完成。首先,您应该报告它,因为这将是HttpClient实现中的一个bug。然后,您可以使用此解决方法:

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);
            }
        }
    }
}
public class limitedtimewaiter
{
公共静态异步任务执行(Func函数、CancellationToken originalToken、int-awaitTime)
{
originalToken.ThrowIfCancellationRequested();
使用(var timeout=CancellationTokenSource.CreateLinkedTokenSource(originalToken))
{
timeout.CancelAfter(TimeSpan.FromSeconds(waittime));
尝试
{
var httpClientTask=函数(timeout.Token);
var timeoutTask=Task.Delay(Timeout.Infinite,Timeout.Token);//这是一个将任务链接到CancellationToken的技巧
var task=wait task.WhenAny(httpClientTask,timeoutTask);
//此时,其中一项任务已完成
//首先,检查我们是否超时
timeout.Token.ThrowIfCancellationRequested();
//如果我们还在那里,这意味着对HttpClient的调用已经完成
返回等待httpClientTask;
}
捕获(操作取消异常错误)
{
抛出新异常(“LimitedTimeWaiter提前结束函数”,err);
}
}
}
}

HttpClient
是否应该在60秒后超时?也许应该,但它不应该,我等待了大约10分钟,没有结果。清理相关计时器我认为这一切都可以通过使用
块将
超时
包装在
中来避免
CancellationTokenSource
IDisposable
.1)我捕获异常是有原因的,
originalToken
用于将下载状态从“下载”设置为“暂停”,并用于对用户操作作出反应。而如果我们不能下载一个文件,下载状态应该变成“错误”。所以“originalToken”不能链接到“timeout”并传递给HttpClient(如果取消后我们再下载一个文件就可以了)。2) 这个解决方案看起来不错,我稍后会检查它。目前,我将HttpClient替换为
HttpWebRequest.Create(url)\HttpWebResponse.GetResponseAsync\response.GetResponseStream.ReadAsync
,它被超时令牌取消。对于此解决方案。。在知道文件大小的情况下,计算要下载的文件的等待时间的最佳方案是什么