可能的HttpClient死锁

时间:2019-07-12 00:27:52

标签: c# .net-core-3.0

我有一个decorator类,该类添加了对访问API的http请求进行速率限制的功能:

public class RateLimitedHttpClient : IHttpClient
{
    public RateLimitedHttpClient(System.Net.Http.HttpClient client)
    {
        _client = client;
        _client.Timeout = TimeSpan.FromMinutes(30);
        //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
    }
    public async Task<string> ReadAsync(string url)
    {
        if (!_sw.IsRunning)
            _sw.Start();

        await Delay();

        using var response = await _client.GetAsync(url);

        return await response.Content.ReadAsStringAsync();
    }

    private async Task Delay()
    {
        var totalElapsed = GetTimeElapsedSinceLastRequest();

        while (totalElapsed < MinTimeBetweenRequests)
        {
            await Task.Delay(MinTimeBetweenRequests - totalElapsed);
            totalElapsed = GetTimeElapsedSinceLastRequest();
        };

        _timeElapsedOfLastHttpRequest = (int)_sw.Elapsed.TotalMilliseconds;
    }

    private int GetTimeElapsedSinceLastRequest()
    {
        return (int)_sw.Elapsed.TotalMilliseconds - _timeElapsedOfLastHttpRequest;
    }

    private readonly System.Net.Http.HttpClient _client;
    private readonly Stopwatch _sw = new Stopwatch();
    private int _timeElapsedOfLastHttpRequest;
    private const int MinTimeBetweenRequests = 100;
}

但是,我注意到在下面指示的行上,我在调试器中收到一条消息,提示the next statement will execute when the current thread returns

var epsDataPoints = await _downloader.GetEPSData(cik);

foreach (var eps in epsDataPoints)
{
     // getting VS2019 debugger message here!
     // assuming that the line above is deadlocking....
     Console.WriteLine($"{cik} :: {eps.DateInterval} :: {eps.EPS}");
}

当我打开任务管理器时,网络带宽变为0,并且所有应用程序都停止了运行,而不是位于上方Console.WriteLine的应用程序。

使用IHttpClient的EPSDownloader类如下:

public class EPSDownloader
{
    public EPSDownloader(IHttpClient client)
    {
        _client = client;
    }

    public async Task<IEnumerable<EPSDataPoint>> GetEPSData(int cik)
    {
        var epsDataPoints = new Dictionary<LocalDate, EPSDataPoint>();
        var reportLinks = await GetReportLinks(cik);

        foreach (var reportLink in reportLinks)
        {
            var xbrlLink = await GetXBRLLink(reportLink);
            var epsData = await GetEPSData(xbrlLink);

            foreach (var eps in epsData)
            {
                if (!epsDataPoints.ContainsKey(eps.DateInterval.End))
                    epsDataPoints.Add(eps.DateInterval.End, eps);
            }
        }

        var list = epsDataPoints.OrderBy(d => d.Key).Select(e => e.Value).ToList();

        return list;
    }

    private async Task<IList<string>> GetReportLinks(int cik)
    {
        // move this url elsewhere
        var url = "https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=" + cik +
                  "&type=10&dateb=&owner=include&count=100";

        var srBody = await _client.ReadAsync(url); // consider moving this to srPage
        var srPage = new SearchResultsPage(srBody);

        return srPage.GetAllReportLinks();
    }

    private async Task<string> GetXBRLLink(string link)
    {
        var url = SEC_HOSTNAME + link;

        var fdBody = await _client.ReadAsync(url);
        var fdPage = new FilingDetailsPage(fdBody);

        return fdPage.GetInstanceDocumentLink();
    }

    private async Task<IList<EPSDataPoint>> GetEPSData(string xbrlLink)
    {
        var xbrlBody = await _client.ReadAsync(SEC_HOSTNAME + xbrlLink);
        var xbrlDoc = new XBRLDocument(xbrlBody);

        return xbrlDoc.GetAllQuarterlyEPSData();
    }

    private readonly IHttpClient _client;
    private const string SEC_HOSTNAME = "https://www.sec.gov";
}

似乎HttpClient存在问题,但我不知道为什么。不会引发任何异常,但我确实偶尔会看到线程已退出,代码为0。

更新:我实际上在应用程序运行时重新启动了计算机,并且在任务管理器将网络速度显示为0并将应用程序停在那儿之前,它又重新正常运行了约20分钟。

0 个答案:

没有答案