Sie haben in Ihrer Frage mehrere Fragen gestellt. Ich werde sie etwas anders aufschlüsseln als Sie. Aber lassen Sie mich zuerst die Frage direkt beantworten.
Wir alle wollen eine Kamera, die leicht, hochwertig und billig ist, aber wie das Sprichwort sagt, können Sie höchstens zwei von diesen drei bekommen. Sie sind hier in der gleichen Situation. Sie möchten eine Lösung, die effizient und sicher ist und Code zwischen synchronen und asynchronen Pfaden teilt. Sie werden nur zwei davon bekommen.
Lassen Sie mich zusammenfassen, warum das so ist. Wir beginnen mit dieser Frage:
Ich habe gesehen, dass Leute sagen, Sie könnten GetAwaiter().GetResult()
eine asynchrone Methode verwenden und sie von Ihrer Synchronisierungsmethode aus aufrufen? Ist dieser Thread in allen Szenarien sicher?
Der Punkt dieser Frage lautet: "Kann ich die synchronen und asynchronen Pfade gemeinsam nutzen, indem der synchrone Pfad einfach synchron auf die asynchrone Version wartet?"
Lassen Sie mich in diesem Punkt ganz klar sein, denn es ist wichtig:
Sie sollten sofort aufhören, Ratschläge von diesen Menschen zu nehmen .
Das ist ein extrem schlechter Rat. Es ist sehr gefährlich, ein Ergebnis einer asynchronen Aufgabe synchron abzurufen, es sei denn, Sie haben Beweise dafür, dass die Aufgabe normal oder abnormal abgeschlossen wurde .
Der Grund, warum dies ein extrem schlechter Rat ist, ist, dieses Szenario zu betrachten. Sie möchten den Rasen mähen, aber Ihr Rasenmähermesser ist gebrochen. Sie entscheiden sich für diesen Workflow:
- Bestellen Sie eine neue Klinge von einer Website. Dies ist eine asynchrone Operation mit hoher Latenz.
- Warten Sie synchron - das heißt, schlafen Sie , bis Sie die Klinge in der Hand haben .
- Überprüfen Sie regelmäßig das Postfach, um festzustellen, ob das Blade angekommen ist.
- Entfernen Sie die Klinge aus der Box. Jetzt haben Sie es in der Hand.
- Installieren Sie die Klinge im Mäher.
- Den Rasen mähen.
Was geschieht? Sie schlafen für immer, weil das Überprüfen der E-Mails jetzt auf etwas beschränkt ist, das nach dem Eintreffen der E-Mails geschieht .
Es ist sehr einfach , in diese Situation zu geraten, wenn Sie synchron auf eine beliebige Aufgabe warten. Für diese Aufgabe ist möglicherweise die Arbeit des Threads geplant, der jetzt wartet , und jetzt wird diese Zukunft niemals eintreffen, weil Sie darauf warten.
Wenn Sie asynchron warten, ist alles in Ordnung! Sie überprüfen regelmäßig die Post und während Sie warten, machen Sie ein Sandwich oder machen Ihre Steuern oder was auch immer; Sie erledigen Ihre Arbeit, während Sie warten.
Warten Sie niemals synchron. Wenn die Aufgabe erledigt ist, ist sie nicht erforderlich . Wenn die Aufgabe nicht erledigt ist, aber geplant ist, dass sie vom aktuellen Thread ausgeführt wird, ist sie ineffizient, da der aktuelle Thread möglicherweise andere Arbeiten erledigt, anstatt zu warten. Wenn die Aufgabe nicht erledigt ist und die Ausführung des aktuellen Threads geplant ist, bleibt sie hängen, um synchron zu warten. Es gibt keinen guten Grund, erneut synchron zu warten, es sei denn, Sie wissen bereits, dass die Aufgabe abgeschlossen ist .
Weitere Informationen zu diesem Thema finden Sie unter
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Stephen erklärt das reale Szenario viel besser als ich.
Betrachten wir nun die "andere Richtung". Können wir Code gemeinsam nutzen, indem die asynchrone Version einfach die synchrone Version in einem Arbeitsthread ausführt?
Das ist aus den folgenden Gründen möglicherweise und in der Tat wahrscheinlich eine schlechte Idee.
Es ist ineffizient, wenn der synchrone Betrieb E / A-Arbeit mit hoher Latenz ist. Dies stellt im Wesentlichen einen Arbeiter ein und lässt diesen schlafen, bis eine Aufgabe erledigt ist. Fäden sind wahnsinnig teuer . Sie verbrauchen standardmäßig mindestens eine Million Byte Adressraum, benötigen Zeit und Betriebssystemressourcen. Sie möchten keinen Thread brennen, der nutzlose Arbeit leistet.
Die synchrone Operation ist möglicherweise nicht threadsicher geschrieben.
Dies ist eine vernünftigere Technik, wenn die Arbeit mit hoher Latenz prozessorgebunden ist, aber wenn dies der Fall ist, möchten Sie sie wahrscheinlich nicht einfach an einen Arbeitsthread übergeben. Sie möchten wahrscheinlich die Task-Parallelbibliothek verwenden, um sie auf so viele CPUs wie möglich zu parallelisieren, Sie möchten wahrscheinlich eine Abbruchlogik und Sie können die synchrone Version nicht einfach dazu bringen, all das zu tun, da es sich dann bereits um die asynchrone Version handelt .
Weiterführende Literatur; wieder erklärt Stephen es sehr deutlich:
Warum nicht Task.Run verwenden:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Weitere "Do and Don't" -Szenarien für Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
Was lässt uns das dann? Beide Techniken zum Teilen von Code führen entweder zu Deadlocks oder zu großen Ineffizienzen. Wir kommen zu dem Schluss, dass Sie eine Wahl treffen müssen. Möchten Sie ein Programm, das effizient und korrekt ist und den Anrufer begeistert, oder möchten Sie einige Tastenanschläge speichern, die durch das Duplizieren einer kleinen Codemenge zwischen dem synchronen und dem asynchronen Pfad entstehen? Ich fürchte, Sie bekommen nicht beides.