简体   繁体   English

HttpClient.GetAsync挂起,然后Internet在执行过程中消失

[英]HttpClient.GetAsync hangs then internet disappears during its execution

I'm downloading lots of small files(size of each about 1 mb) in a cycle and I want this cycle to break in case of lost internet connection. 我正在一个周期内下载许多小文件(每个文件的大小约为1 mb),并且我希望在失去互联网连接的情况下中断此周期。 But httpClient.GetAsync hangs and doesn't thow an exception if I switch my wi-fi off(or take out the cord) while it is being executed. 但是httpClient.GetAsync挂起,并且如果我在执行Wi-Fi时将其关闭(或拔出电源线),也不会引发异常。 Also it doesn't get canceled if I'm using CancelationToken (if it is canceled prior to httpClient.GetAsync it gets canceled, but I need to cancel it during the execution). 如果我使用httpClient.GetAsync ,它也不会被取消(如果它在httpClient.GetAsync之前被取消, httpClient.GetAsync它会被取消,但是我需要在执行过程中将其取消)。

My experience shows what if internet disappears before httpClient.GetAsync(url, cancellationToken); 我的经验表明,如果Internet在httpClient.GetAsync(url, cancellationToken);之前消失,该httpClient.GetAsync(url, cancellationToken); is called it will throw an exceprion, but if internet disappears then httpClient.GetAsync(url, cancellationToken); 被调用会抛出异常,但如果互联网消失,则httpClient.GetAsync(url, cancellationToken); already started to execute then it will hang endlessly and even if I trigger set the CancelationTokenSource.Cancel() the operation wouldn't cancel. 已经开始执行,那么它将无限期挂起,即使我触发设置CancelationTokenSource.Cancel() ,操作也不会取消。

The function with HttpClient usage 具有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();
        }

is being called from cycle using line 使用行从循环中调用

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

while here is the LimitedTimeAwaiter code 这是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);
            }
        }
    }

While I cancel the token given to httpClient.GetAsync doesn't throw an OperationCanceledException and hangs endlessly. 当我取消分配给httpClient.GetAsync的令牌时, httpClient.GetAsync不会引发OperationCanceledException,并且会无休止地挂起。 I am searching for a way to abort it if doesnt return a value in a limited ammount of time. 我正在寻找一种方法来中止它,如果在有限的时间内没有返回值。

I'm not 100% sure of what you're trying to do, but there's a flaw in your LimitedTimeAwaiter. 我不确定您要做什么,但您的LimitedTimeAwaiter有一个缺陷。 You're not actually giving the originalToken to the HttpClient, therefore it won't be used for cancellation. 您实际上并没有将originalToken给HttpClient,因此不会将其用于取消。 To fix that, you should link your two tokens: 要解决此问题,您应该链接两个令牌:

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);
        }
    }
}

(also not entirely sure why you catch the exception but that's beside the point). (也不能完全确定为什么要捕获异常,但是这很重要)。

Now I'm going to assume your issue is really that the task returned by HttpClient does not complete even when you cancel the right token. 现在,我将假设您的问题确实是,即使您取消了正确的令牌,HttpClient返回的任务也不会完成。 First, you should report it because that would be a bug in the HttpClient implementation. 首先,您应该报告它,因为那可能是HttpClient实现中的错误。 Then you can use this workaround: 然后,您可以使用以下解决方法:

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);
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM