none
Task, der Zwischenergebnisse liefert RRS feed

  • Allgemeine Diskussion

  • Hi,

    ich möchte eine Schnittstelle anbieten, deren Funktionsaufrufe asynchron sind. Mit der TPL und dem Typ Task<T> geht dies, was ich gesehen habe sehr elegant. Der Task sucht in meinem Fall nach Informationen. Die Informationsbeschaffung kann evtl. einige Zeit in Anspruch nehmen und ich möchte dem Konsumenten Zwischenergebnisse liefern. Hierfür könnte der Typ Progress<T> genutzt werden. Ich bin am überlegen, wie die Schnittstelle für diesen Aufruf aussehen könnte. Mein ersteer Ansatz wäre folgender:

    public Task<MeinResultObjekt> MeineMethodeAsnc(out Progress<MeinResultObjekt> zwischenStand);

    Die Verwendung würde wie folgt aussehen:

    Progress<MeinResultObject> zwischenStand = null;
    var t = meinSuchObject.MeineMethodeAsnc(out zwischenStand);
    zwischenStand.ProgressChanged += (Sender, meinResultObjekt) => { /* mache etwas mit meinResultObjekt*/ };
    t.Start();

    Kennt Ihr hierfür einen schöneren / besseren Ansatz?

    Viele Grüße,
    Christian

    Donnerstag, 16. Mai 2013 13:49

Alle Antworten

  • Hallo Christian,

    deine Frage ist leider etwas unpräzise. Abhängig vom erwünschten Effekt könntest Du auch folgendes verwenden:

    var progress = new Progress<MeinResultObjekt>(o => Debug.WriteLine(o.ToString()) );
    var t = await MeineMethodeAsync(progress);

    P.S. Natürlich musst Du in diesem Fall die Signatur deiner asynchronen Methode leicht abändern:

    Task<MeinResultObjekt> MeineMethodeAsync(IProgress<MeinResultObjekt> progress) {}

    Jetzt kannst Du in der Methode über verschiedene Zwischenstände berichten:

    progress.Report(new MeinResultObjekt());
    Thread.Sleep(1000); // Aktivitäts-Simulation
    progress.Report(new MeinResultObjekt());
    Thread.Sleep(1000); // Aktivitäts-Simulation
    

    Ich kann mir nicht sicher sein, ob syntaktisch-ästhetische Wünsche oder funktionale Überlegungen im Vordergrund deiner Frage stehen. Aber dies läßt sich sicherlich im weiteren Threadverlauf klären.

    Eines kann ich dir aber schon jetzt verraten: Progress<T> ist eine Klasse die IProgress<T> implementiert und einen Delegaten sowie eine Referenz auf das aktuelle Synchronisierungskontext kapselt. Progress<T> speichert in seinem Konstruktor eine Referenz auf SynchronizationContext.Current. Dadurch kann der ProgressChanged-Ereignishandler auf dem Erstellerthread aufgerufen werden (normalerweise der UI-Thread). So werden Fehler bei threadübergreifenden Zugriffen vermieden (sonst könntest Du ja auch einfach ein Action<T> als Parameter übergeben).

    In deinem obigen Code-Beispiel aber wird die Instanzierung des Progress<T>-Objekts
    an die asynchrone Methode delegiert (über ein out-Parameter, der in der asynchronen Methode zugewiesen werden muss), weswegen u.U. der falsche Synchronisationskontext gekapselt wird!

    Wenn die asynchrone Methode nämlich auf einem anderen Thread läuft, wird SynchronizationContext.Current über den falschen Kontext mit der UI zu kommunizieren versuchen und der ProgressChanged-Handler wird nicht auf dem UI-Thread aufgerufen werden. Das Resultat ist eine InvalidOperationException wegen threadübergreifendem Zugriff.

    Gruß
    Marcel


    Donnerstag, 16. Mai 2013 15:08
    Moderator
  • Hi Marcel,

    vielen Dank für die ausführliche Rückmeldung und die tolle Erklärung bezüglich Progress<T> und dem SynchronizationContext.

    Du hast erwähnt, dass meine Frage ein bisschen unpräzise ist.  Welche weitere Information wäre bitte relevant?

    Viele Grüße,
    Christian

    Donnerstag, 16. Mai 2013 15:23
  • Hi Christian,

    Sehr gerne. Bezüglich deiner Rückfrage: Du hattest ja nach einem schöneren/besseren Ansatz gefragt. Schöner oder besser in bezug auf was? Über IProgress.Report() z.B. läßt sich der ProgressChanged-EventHandler ganz vermeiden. Diese Sprachmittel-Ökonomie würden vermutlich manche als "schön" empfinden, andere als "besser", wiederum andere würden sagen: Ich brauch den ProgressChanged-Handler in meinem besonderen Einsatzszenario und wieder andere würden sagen: Ich will die UI gar nicht aktualisieren, also verzichte ich ganz auf IProgress<T> und übergebe an die asynchrone Methode einfach nur ein o => Console.WriteLine(o), ein Action<T> das geht auch. Mit anderen Worten: Ich weiß nicht genau worum es dir geht, was genau erwartest Du in welchem Kontext?

    Viele Grüße
    Marcel

    Donnerstag, 16. Mai 2013 16:01
    Moderator
  • Hi Marcel,

    Schöner oder besser in Bezug auf was?

    Ich habe hierbei an evtl. bereits bewährte Muster gedacht, die sich bereits bewährt haben, bzw. nach Informationen gesucht, was noch berücksichtigt werden müsste, wie z.B. Dein Hinweis auf ThreadContext, der im ctor von Progress<T> ermittelt wird.

    Da es sich beim Konsumenten der Schnittstelle um ein UI (WPF) handelt, ist diese Information für mich absolut relevant.

    Vielen Dank für die tolle Hilfe!!

    Christian

    Freitag, 17. Mai 2013 06:37