Asynchroner Code scheint teilweise blockiert zu sein, wenn mehrere asynchrone Tasks in einer Zeile aufgerufen werden (mit HttpClient)

8

Ich habe versucht, C # async mit HttpClient zu lernen und bin auf ein Problem gestoßen, bei dem ich nicht herausfinden kann, was passiert.

Was ich tun möchte:

  • Senden Sie mehrere HttpClient-Anfragen auf einmal und lassen Sie die Antworten einzeln bearbeiten, sobald sie zurückkommen. Dieser Prozess wird für immer in einer Schleife wiederholt (d. H. Eine neue Menge von Anforderungen wird alle paar Sekunden gesendet). Die Verarbeitung der Ergebnisse ist nicht CPU-intensiv und dauert höchstens einige Millisekunden pro Antwort.
  • Menge ist 5-50 Beiträge in einer Instanz
  • Es ist ihnen egal, in welcher Reihenfolge sie zurückkommen, aber jede Anfrage / Antwort muss so schnell wie möglich bearbeitet werden, ohne zu blockieren.

Das Problem:

  • Immer wenn ich eine Liste von Anfragen zusammenschicke, scheinen die Antworten etwas länger zu dauern, aber sie sollten alle ungefähr gleich sein. z.B .:

    Benötigte Zeit: 67.9609 Millisekunden
    Benötigte Zeit: 89.2413 Millisekunden
    Benötigte Zeit: 100.2345 Millisekunden
    Benötigte Zeit: 117.7308 Millisekunden
    Benötigte Zeit: 127.6843 Millisekunden
    Benötigte Zeit: 132.3066 Millisekunden
    Benötigte Zeit: 157.3057 Millisekunden

Wenn sie sollten alle nur etwa 70ms sein ... Dies wird für jede neue Charge wiederholt; der erste ist schnell, dann wird jeder weitere verlangsamt.

Mein Code
Es gibt 3 Hauptteile. Ich habe es so gut wie möglich vereinfacht, um mich auf das Problem zu konzentrieren (Downloader.cs ist das, was ich zeitlich festgelegt habe)

  • Downloader.cs Dies enthält die Methode, die die eigentliche http-Abfrage ausführt:

    %Vor%
  • QueryAgent.cs Initialisiert einen neuen Downloader und enthält die asynchrone Schleifenfunktion des Aufrufers und die in einer Schleife aufgerufene asynchrone Methode.
    Ich muss so schnell wie möglich neue Anfragen senden, abhängig vom Ergebnis der letzten Antworten (im Prinzip kann sich die Rate, mit der Anfragen eingehen, ändern, so dass ich am Ende der Sitzung keine Task.delay () ausführen kann) While-Schleife)

    %Vor%
  • Program.cs Dies ist nur ein Konsolenprogramm, das eine Aufgabe einmal in der asynchronen Funktion in QueryAgent und dann eine Console.Readline () am Ende aufruft, um das Programm zu beenden. Ich initialisiere den HttpClient auch hier. Ich rufe DownloadLoop () einmal als solches auf:

    %Vor%

Ich habe den Verdacht, dass es etwas damit zu tun hat, wie ich eine neue Aufgabe in der for-Schleife in DownloadLoopAsync () aufruft, aber das macht keinen Sinn, weil ich das angenommen habe Jede neue Aufgabe würde von selbst bearbeitet werden.

Jeder Rat wird sehr geschätzt, da ich nach der Ziegelmauer auf die Ziegelmauer geschlagen habe, um zu versuchen, dass dies wie beabsichtigt funktioniert.

Danke.

EDIT 1
 Ich hatte gerade eine Unordnung in der aufrufenden Task-Schleife. Ich habe gewechselt:

%Vor%

bis

%Vor%

was nun funktioniert wie erwartet:

%Vor%

Jetzt bin ich noch verwirrter, warum das scheint zu funktionieren!

BEARBEITEN 2
Der Timing-Code existiert innerhalb von Downloader.cs wie folgt:

%Vor%

Obwohl die Task warten.Delay (1); scheint "den Trick zu machen", es ist keine ideale Lösung noch verstehe ich, warum es funktioniert - vielleicht gibt es irgendwie die CPU Zeit, um die RequestNewDataAsync Task zu initialisieren und verhindert, dass eine Art von Lock-up-Vereinbarung? Ich kann nur raten ... Es bedeutet auch, dass es die Schleife auf 1000 Hz begrenzt, was schnell ist, aber auch eine Grenze, da Task.Delay () nur ganzzahlige Werte akzeptiert, von denen 1 das Minimum ist.

EDIT 3
Ein bisschen mehr lesen (ich bin noch relativ neu in all dem!), Ist es vielleicht das Beste, den Threadpool zu verwenden (10 bis 100 von kurzlebigen Aufgaben)? Würde dies jedoch einen übermäßigen Aufwand verursachen (1 MB pro neuer Aufgabe?)? oder ist das wieder etwas anderes.

EDIT 4
Habe ein bisschen mehr experimentiert, indem ich die Task.Delay (1) an verschiedenen Stellen platziert habe.

  • [Verlangsamung tritt auf] Platzierung vor oder nach httpClient.PostAsync () in Downloader.cs
  • [Verlangsamung tritt auf] Platzieren vor oder nach dem Warteaufruf in RequestNewDataAsync ()
  • [keine Verlangsamung / erwartete Leistung] Platzierung vor oder nach dem Task-Aufruf in DownloadLoopAsync ()
undercurrent 25.03.2015, 11:48
quelle

2 Antworten

6

Wenn Sie await ausführen, wird der nachfolgende Code erst ausgeführt, nachdem Sie "erwartet" haben.

Um mehrere Abfragen gleichzeitig zu senden, müssen Sie für jede Abfrage ein neues Task erstellen und dann alle zusammen abwarten.

%Vor%     
shr 25.03.2015 12:02
quelle
1

Wahrscheinlich werden Sie nur Ihr Netzwerk oder den Remote-Server mit Anfragen sättigen. Ihre Endlosschleife wartet nicht darauf, dass der vorherige Stapel abgeschlossen wird, bevor der nächste Stapel gestartet wird. Daher werden im Lauf der Zeit Stapel schneller ausgegeben, als sie abschließen können, was zu einem Rückstand führt.

Hier ist eine modifizierte Schleife, die darauf wartet, dass ein Batch abgeschlossen wird, bevor der nächste Batch gestartet wird. Beachten Sie, dass es nicht mehr auf Task.Delay wartet und stattdessen die Sammlung von Download-Aufgaben erwartet.

%Vor%

Beachten Sie, dass Sie eine Racebedingung haben, in der Sie die Ergebnisse der verarbeiteten Daten speichern. Dies kann oder kann kein Problem für Sie sein:

%Vor%

Ich habe auch Ihr Hauptprogramm geändert, um auf den letzten Stapel zu warten, der ausgeführt wird, nachdem der Benutzer eine Taste gedrückt hat:

%Vor%     
Brandon 25.03.2015 16:04
quelle