HttpClient SendAsync blockiert den Hauptthread

Aug 21 2020

Ich habe eine kleine Winforms-Anwendung geschrieben, die http-Anfragen an jede IP-Adresse in meinem lokalen Netzwerk sendet, um ein bestimmtes Gerät von mir zu entdecken. Auf meiner speziellen Subnetzmaske sind das 512 Adressen. Ich habe dies mit backGroundWorker geschrieben, wollte aber httpClient und das Async / Await-Muster ausprobieren, um dasselbe zu erreichen. Der folgende Code verwendet eine einzelne Instanz von httpClient und ich warte, bis alle Anforderungen abgeschlossen sind. Dieses Problem ist, dass der Haupt-Thread blockiert wird. Ich weiß das, weil ich eine Bildbox + Lade-GIF habe und es nicht einheitlich animiert. Ich habe die GetAsync-Methode wie hier vorgeschlagen in einen Task.Run eingefügt, aber das hat auch nicht funktioniert.

    private async void button1_Click(object sender, EventArgs e)
    {
        var addresses = networkUtils.generateIPRange..
        await MakeMultipleHttpRequests(addresses);
    }


    public async Task MakeMultipleHttpRequests(IPAddress[] addresses)
    {
        List<Task<HttpResponseMessage>> httpTasks = new List<Task<HttpResponseMessage>>();
        foreach (var address in addresses)
        {
            Task<HttpResponseMessage> response = MakeHttpGetRequest(address.ToString());
            httpTasks.Add(response);
        }
        try
        {
            if (httpTasks.ToArray().Length != 0)
            {
                await Task.WhenAll(httpTasks.ToArray());
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("\thttp tasks did not complete Exception : {0}", ex.Message);
        }

    }

    private async Task<HttpResponseMessage> MakeHttpGetRequest(string address)
    {
        var url = string.Format("http://{0}/getStatus", address);
        var cts = new System.Threading.CancellationTokenSource();
        cts.CancelAfter(TimeSpan.FromSeconds(10));
        HttpResponseMessage response = null;
        var request = new HttpRequestMessage(HttpMethod.Get, url);
        response = await httpClient.SendAsync(request, cts.Token);
        return response;
    }

Ich habe hier ein ähnliches Problem gelesen , aber mein GUI-Thread macht nicht viel. Ich habe hier gelesen , dass mir vielleicht die Fäden ausgehen. Ist dies das Problem, wie kann ich es beheben? Ich weiß, dass es das Senden von Async ist, denn wenn ich den Code durch die unten stehende einfache Aufgabe ersetze, gibt es keine Blockierung.

    await Task.Run(() =>
    {
       Thread.Sleep(1000);
    });

Antworten

2 Andy Aug 21 2020 at 05:31

Eines der Probleme hierbei ist, dass Sie in schneller Folge nacheinander mehr als 500 Aufgaben erstellen und ein Zeitlimit außerhalb der Aufgabenerstellung festlegen.

Nur weil Sie mehr als 500 Aufgaben ausführen möchten, bedeutet dies nicht, dass alle mehr als 500 Aufgaben gleichzeitig ausgeführt werden. Sie werden in die Warteschlange gestellt und ausgeführt, wenn der Scheduler dies für möglich hält.

Sie legen zum Zeitpunkt der Erstellung eine Zeitüberschreitung von 10 Sekunden fest. Aber sie könnten 10 Sekunden im Scheduler sitzen, bevor sie überhaupt ausgeführt werden.

Wenn Sie möchten, dass Ihre HTTP-Anforderungen eine organische Zeitüberschreitung aufweisen, können Sie dies folgendermaßen tun, wenn Sie Folgendes erstellen HttpClient:

private static readonly HttpClient _httpClient = new HttpClient
{
    Timeout = TimeSpan.FromSeconds(10)
};

Wenn Sie das Zeitlimit auf das verschieben HttpClient, sollte Ihre Methode nun folgendermaßen aussehen:

private static Task<HttpResponseMessage> MakeHttpGetRequest(string address)
{
    return _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, new UriBuilder
    {
        Host = address,
        Path = "getStatus"
    }.Uri));
}

Versuchen Sie, diese Methode zu verwenden, und prüfen Sie, ob sie Ihr Sperrproblem im Debug-Modus verbessert.

Was das Problem betrifft , das Sie hatten : Es blockiert, weil Sie sich im Debug-Modus befinden und der Debugger 500 Mal gleichzeitig versucht, "Hey, Sie haben eine Ausnahme" zu sagen, weil alle gleichzeitig erzeugt wurden . Führen Sie es im Release-Modus aus und prüfen Sie, ob es immer noch abstürzt.

Was ich tun würde, ist Ihre Operationen zu stapeln. Mach 20, dann warte bis diese 20 fertig sind, mach 20 weitere, so weiter und so fort.

Wenn Sie eine raffinierte Methode zum Stapeln von Aufgaben sehen möchten, lassen Sie es mich wissen, und ich würde es Ihnen gerne zeigen.

1 PauloMorgado Aug 21 2020 at 17:03

In .NET Framework wird die Anzahl der Verbindungen zu einem Server von der ServicePointManager-Klasse gesteuert .

Für einen Client beträgt das Standardverbindungslimit für Clientprozesse 2 .

Unabhängig davon, wie viele HttpClient.SendAsync-Aufrufe Sie ausführen, sind nur 2 gleichzeitig aktiv.

Sie können die Verbindungen jedoch selbst verwalten.

In .NET Core ist hier nicht das Konzept des Service Point Managers und das entsprechende Standardlimit ist int.MaxValue.