TAP - Synchrone und Asynchrone Arbeit in einer Methode

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    TAP - Synchrone und Asynchrone Arbeit in einer Methode

    Hallo,

    ich möchte in einer API gerne TAP-Support bereitstellen und die Objekte die ich nutze unterstützen dies bereits. In meiner API Methode arbeite ich allerdings auch noch eine Reihe synchroner Aufgaben ab, ehe ich die asynchrone Methode verwende. Nun stellt sich mir die Frage ob das überhaupt gerechtfertigt ist meine Methode *Async zu benennen, da ja auch synchrone Arbeit ausgeführt wird. Der Nutzer würde sich ja vermutlich wundern wieso sein Caller-Thread teilweiße blockiert wird, obwohl er eine async Methode aufgerufen hat.

    C#-Quellcode

    1. public async Task<IList<Entity>> ExecuteQueryAsync(IQuery query)
    2. {
    3. var sqlQuery = query.CompileSql(); // synchrone Aufgabe
    4. var result = await dbConnection.ExecuteQueryAsync(sqlQuery);
    5. // ...
    6. return entityList;
    7. }


    Ich könnte natürlich mit Task.Run arbeiten, ich habe jedoch gelesen das es bad practice wäre, da ich ja einfach einen anderen Threadpool Thread blockieren würde. (siehe stackoverflow.com/a/35558205)

    Wie würdet ihr das lösen?
    Also ich hätte das wirklich einfach mit Task.Run gelöst. Habe das auch bei mir so gemacht, nur war da noch ein bisschen mehr synchroner Code, der dabei war. Wenn das allerdings Bad Practice wäre, dann würde mich ebenso brennend interessieren, was die Alternative ist. Man kann ja nicht verlangen, dass dann auch wirklich alles, was in der Methode aufgerufen wird 100% asynchron ist und awaited werden kann.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Du einfach einen neuen Thread blockierst, somit kannst du auch den aktuellen Thread blockieren nur das du noch Ressourcen sparst.

    Weiß @~blaze~ etwas?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ThuCommix“ ()

    Hi
    ich bin mit der ganzen async-await-Geschichte nicht sonderlich firm, da ich in letzter (langer) Zeit kaum noch was programmiert habe, aber gibt es einen Grund, warum man die Query nicht synchron in einem anderen Task ausführen sollte? Also CompileSql und ExecuteQuery in einem einzigen Task?

    Viele Grüße
    ~blaze~
    Weil es theoretisch nichts bringt:

    - Thread A startet die Aufgabe
    - Aufgabe wird in Thread B aus dem ThreadPool ausgelagert der nun blockert wird, Thread A wird währendessen dem ThreadPool zurückgeführt
    - Thread B wird dem ThreadPool zurück geführt
    - Thread A macht etwas mit den Ergebnis aus Thread B und startet eine weitere async Aufgabe

    Wie man sieht habe ich die synchrone Aufgabe nur zwischen den Threads hin und hergeschaufelt, wobei ich zusätzliche Ressourcen für die Context Switche aufgebracht hab.

    Mir geht es ja darum ob man Methode Async heißen sollte oder nicht. Eine Async IO Aufgabe blockiert nicht einen Thread aus Pool, sondern gibt den aktuellen Frei und feuert einen interrupt wenn das Gerät z.B. Festplatte fertig mit lesen ist. Ich suggeriere den Benutzer das er mit meiner Methode Ressourcen schonend arbeitet, obwohl ich einfach nur einen anderen Thread blockiere.

    tldr; Muss eine als Async bezeichnete Methode vollständig Async sein?
    Ich kenne mich mit den Konventionen nicht aus, würde aber intuitiv sagen, dass die Vorbereitung zulässig ist. Es hat beides Vorteile und Nachteile bzgl. Komplexität des Programms und Handhabbarkeit und "lightweight"-Verhalten, da ja der Aufrufer quasi keine Zeit verbraucht.
    Aber ich kann nicht beurteilen, ob Async-Methoden vollständig Async sein müssen.

    Das Await bringt dir ja aber eigentlich auch nur etwas, wenn du während dem asynchronen Vorgang weitere Vorgänge durchführst.
    Wenn du CompileQuery und ExecuteQuery innerhalb des asynchronen Vorgangs aufrufst und diesen als gestarteten Task zurückgibst, hast du doch eigentlich kein await (außer halt beim Aufrufer). Da würde nur dein Thread B die innere Aufgabe abarbeiten und A nicht blockiert.

    Viele Grüße
    ~blaze~

    ThuCommix schrieb:

    Muss eine als Async bezeichnete Methode vollständig Async sein

    Ich würde sagen ja, weil sonst kommt es zu sehr unterschiedlichen Ergebnissen und Problemen. ConfigureAwait() kommt ja auch noch dazu, wenn der UI Thread den Task1 aufruft, der mit ConfigureAwait(false) zurückgibt und dann deine synchrone Methode aufgerufen wird, wird diese ja trotzdem asynchron aufgerufen (zumindest im Verhältnis zum UI Thread). Ich programmiere relativ viel mit asynchroner Programmierung und benutze in solchen Fällen immer Task.Run(), bei mir ist das vorallem bei Hash Berechnung immer das Problem. In dieser StackOverflow Antwort wird das auch empfohlen: stackoverflow.com/questions/27…ynchronous-sha256-hashing.

    Was halt außerdem ein riesigen Problem ist, ist dieses ConfigureAwait(false), da sparst du dir auch eine menge Thread-Switching bei deiner Library, wenn du das noch nicht überall hast, wird dir das wesentlich mehr bringen als die Entscheidung zwischen Task.Run().
    Mfg
    Vincent

    ThuCommix schrieb:

    Wie würdet ihr das lösen?
    Ich würde mit Task.Run() arbeiten, ob jemand auf StackOverflow das für BadPractice hält oder nicht.
    Weil ich stimme dir zu, eine async-Methode sollte man keinesfalls in einer Dll anbieten, wenn sie nicht wirklich async ist, sondern eben doch blockiert.
    Und gleichzeitig aber trotzdem awaited werden muss - also diese Mischung geht imo nicht in einer Dll.

    Also wenn das synchrone Compilen nur Millisekunden kostet, dann wärs imo egal, aber wenn da wirklich der Aufrufer-Thread spürbar stockt, wäre das iwie ein nicht eingehaltenes Versprechen.
    Und Schnittstellen-Versprechen einzuhalten ist eine ganze Kategorie wichtiger als vermeintliche oder auch wirkliche Threadpool-Optimierung.

    Oder du schmeisst die Methode raus, und bietest iwas anneres an, wo async und nicht-async nicht vermischt werden.