Wie erstelle ich eine natürlich asynchrone Methode, wenn interne Aufrufe nicht natürlich asynchron sind?

8

In diesem Szenario muss System A eine Nachricht an System B senden. Der folgende Code zeigt ein Beispiel dafür, wie dies durchgeführt wurde:

%Vor%

Aus Gründen, die außerhalb des Umfangs dieser Frage liegen, haben wir uns entschieden, von Wcf auf die Verwendung von Raw http-Streams umzusteigen, aber wir brauchten beide Seite an Seite, um Metriken zu sammeln und sie zu testen. Also habe ich eine neue IExecutionStrategy Implementierung erstellt, um das zu handhaben:

%Vor%

Im Grunde genommen war die einzige Möglichkeit, dies asynchron zu bekommen, es in ein Task.Run() zu verpacken, so dass die Web-Anfrage nicht blockiert wurde. ( Hinweis : Aufgrund der einzigartigen Stream-Manipulationsanforderungen beim Senden und Empfangen ist es nicht einfach, dies zu implementieren, dies ist in HttpClient , und selbst wenn es möglich ist, ist diese Tatsache für diese Frage außerhalb des Geltungsbereichs) .

Wir dachten, das wäre in Ordnung, bis wir Stephen Cleary mehrere Blog-Posts über wie Task.Run() schlecht ist, sowohl im Bibliothekscode als auch in Asp.Net-Anwendungen. Das macht Sinn für mich.

Was keinen Sinn ergibt, ist, wie Sie einen natürlich asynchronen Aufruf implementieren, wenn die Third-Party-Bibliothek keine asynchrone Bewegung unterstützt. Wenn Sie zum Beispiel HttpClient.GetStreamAsync() verwenden, bedeutet das, dass es für asynchrone Operationen besser ist als Task.Run(() => HttpClient.GetStream()) , und gibt es eine Möglichkeit, dies für nicht-asynchrone Bibliotheken von Drittanbietern zu beheben?

    
KallDrexx 11.07.2014, 18:39
quelle

2 Antworten

9

HttpClient.GetStreamAsync ist eine reine asynchrone Methode, was bedeutet, dass während des Aufrufs keine neuen Threads eingeführt werden und bei Verwendung in Kombination mit await die Steuerung zurück an den Aufrufer liefert, bis die IO-Anforderung abgeschlossen ist. Dies wird gut skalieren, da Sie den ThreadPool-Thread freigeben, der die Operation aufgerufen hat, um während der Ausführung der Anfrage so viel Arbeit zu leisten, sodass Ihr Server in der Zwischenzeit mehr Anfragen bearbeiten kann.

Im Gegensatz dazu wird die Verwendung eines dedizierten Threads (sync über async), nur um einen blockierenden IO-Request zu machen, definitiv nicht gut skalieren und kann eventuell zu einem Verhungern führen, wenn die Ausführungszeit lang genug ist.

Bearbeiten

Die wirklich asynchrone Art der XXXAsync -Implementierung stammt vom Netzwerkgerätetreiber, der dem Betriebssystem einen asynchronen Endpunkt liefert. Unter der Decke der WinHTTP (Danke @Noseratio für die Korrektur) Bibliothek wird für die asynchronen Operationen verwendet. Dies bedeutet, dass ein E / A-Anforderungspaket (IRP) generiert und an den Gerätetreiber übergeben wird. Sobald die Anforderung abgeschlossen ist, wird eine CPU-Unterbrechung auftreten, die schließlich dazu führt, dass der registrierte Rückruf aufgerufen wird. Sie können diese Beispiele betrachten: Verwenden von WinInet HTTP-Funktionen in voller Asynchronous Mode oder Windows mit C ++ - Asynchroner WinHTTP für asynchrone Beispiele, und natürlich lesen Sie die ausgezeichnete There Is No Thread von Stephan Cleary. Sie können es nativ implementieren und in einen verwalteten Wrapper einbinden.

    
Yuval Itzchakov 11.07.2014, 19:01
quelle
6

Kurze Antwort

Verwenden Sie async-await , wenn die Operation wirklich asynchron ist. Wenn nicht, dann tu das nicht so.

Lange Antwort

Asynchrone Operationen haben nur zwei echte Vorteile: Skalierbarkeit und Entlastung . Skalierbarkeit ist hauptsächlich für die Serverseite, während Offloading für "spezielle" Threads verwendet wird (meistens GUI threads).

Wenn Sie auf eine synchrone Bibliothek angewiesen sind und nichts dagegen tun können, wird die Skalierbarkeit <%> durch die Verwendung von Task.Run nicht verbessert (und kann dies sogar behindern). und Sie sind nur mit dem Ausladen übrig. async reduziert die Ressourcennutzung nur, wenn die Operation von Natur aus asynchron ist, wenn kein Thread während des eigentlichen asynchronen Teils (Netzwerk, Festplatte, etc.)

Wenn Sie eine Rich-Client-Anwendung entwickeln, verwenden Sie "sync über async" mit async-await . Aber für jede andere Art von Anwendung gibt es keinen Grund async-await zu verwenden, und ich würde es dagegen empfehlen.

    
i3arnon 11.07.2014 18:58
quelle