none
TaskComplitationSource Task sofort beenden

    Frage

  • Hallo!

    Nun ist für mich doch noch ein Problem mit der TaskComplitationSource aufgetreten.

    Wie beendet man den Task, (sofort) beim Auftreten einer Exception? Bei throw ... geht es automatisch.

    Ich habe das Beispiel aus der vorangegangenen Frage (async Exception durchreichen ) genommen und eine weitere Exception hinzugefügt:

    // Starten des asynchronen Hintergrund-Threads, der Fehler auslöst
    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(5000);
        tcs1.SetException(new InvalidOperationException("Simulierter 1. Fehler"));
        Thread.Sleep(2000);
        tcs1.SetException(new InvalidOperationException("Simulierter 2. Fehler"));
    });

    Bei der Ausführung kommt die Fehlermeldung:

    ... weil die Ausführung nach der 1. Exception nicht abgebrochen wird.

    Wie kann man dies erreichen?


    • Bearbeitet perlfred Dienstag, 13. Februar 2018 09:49
    Dienstag, 13. Februar 2018 09:47

Antworten

  • Hi,
    der Programmierer kann doch das Programm so gestalten, dass nach dem Werfen des Fehler nichts mehr gemacht wird. Wer zwingt Dich nach SetException noch weitere Dinge zu machen? Üblicherweise wird der gesamtes asynchrone Code in Try-Catch eingebaut und SetException lediglich in catch-Block ausgeführt. Zu beachten dabei ist aber, dass nach dem SetException u.U. noch aufzuräumen ist, z.B. Datenbankverbindung schließen. Das kann man über using oder im finally machen. In beiden Fällen werden diesbezügliche Arbeiten auch nach dem SetException ausgeführt.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Donnerstag, 15. Februar 2018 08:06
    Mittwoch, 14. Februar 2018 16:52

Alle Antworten

  • Hi,
    welchen Sinn soll solch eine Arbeitsweise haben? Mit dem ersten Fehler wird signalisiert, dass in der Verarbeitung ein Fehler aufgetreten ist. Was soll dann der zweite Fehler bewirken, solange der erste Fehler noch nicht verarbeitet wurde?

    Wenn Du wirklich mehrere Fehler "sammeln" willst, dann nutze eingebettete Fehlerobjekte oder nutze eine andere Technik wie beispielsweise eine Queue von Exception-Objekten, die ggf. über ein Ereignis an einen Abonnenten übergeben werden. 

     

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Dienstag, 13. Februar 2018 11:05
  • Hallo Peter!

    Den 2. Fehler habe ich nur "eingebaut", um zu zeigen, dass auch nach dem ersten Fehler im Code weitergegangen wird. Ein Console.WritLine() hätte sicherlich den gleichen Zweck erfüllt.

    Wie kann aber der Ablauf beim Auftreten eines Fehlers unterbrochen werden???

    ---------------------------------------------

    Es wird noch ein bisschen "merkwürdiger" ...

    Ich habe den Code mal so geändert:

    // Starten des asynchronen Hintergrund-Threads, der Fehler auslöst
    Task.Factory.StartNew(() =>
    {
        Console.WriteLine("\nProgramm gestartet.");
        Thread.Sleep(2000);
        tcs1.SetException(new InvalidOperationException("Simulierter 1. Fehler"));
        Thread.Sleep(2000);
        Console.WriteLine("\n\n!!!Programm geht weiter!!!");
    });
    

    Wenn man das Programm debuggt, wird folgendes angezeigt:

    Wenn man es ohne Debugger startet folgendes:

    • Bearbeitet perlfred Dienstag, 13. Februar 2018 13:05
    Dienstag, 13. Februar 2018 12:45
  • Hi,
    egal, wie übersetze und starte, ich bekomme immer das gleiche Ergebnis. Da muss bei Dir noch etwas anderes sein.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Dienstag, 13. Februar 2018 21:11
  • Hallo Peter!

    Ich verwende VS 2015 mit folgendem Code:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program34
        {
            static void Main(string[] args)
            {
                try
                {
                    Demo c = new Demo();
                    c.Execute();
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                Console.WriteLine("Fertig, Abschluss mit beliebiger Taste");
                Console.ReadKey();
            }
    
            class Demo
            {
                internal void Execute()
                {
                    TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
                    Task<int> t1 = tcs1.Task;
    
                    // Starten des asynchronen Hintergrund-Threads, der Fehler auslöst
                    Task.Factory.StartNew(() =>
                    {
                        Console.WriteLine("\nProgramm gestartet.");
                        Thread.Sleep(2000);
                        tcs1.SetException(new InvalidOperationException("\nSimulierter 1. Fehler"));
                        Thread.Sleep(2000);
                        Console.WriteLine("\n\n!!!Programm geht weiter!!!");
                    });
    
                    // Ergebnis abwarten
                    try
                    {
                        int result = t1.Result;
                        Console.WriteLine("\nErgebnis erhalten.");
                    }
                    catch (AggregateException e)
                    {
                        Console.WriteLine("\nFehler aufgetreten:");
                        Exception ex2 = e;
                        while (ex2 != null)
                        {
                            Console.WriteLine("\n-------------------------------------------------\n{0}", ex2.ToString());
                            ex2 = ex2.InnerException;
                        }
                    }
                }
            }
        }
    }
    

    Und erhalte folgende Ausgabe:

    Wobei die letzte Zeile "!!!Programm geht weiter!!!" erst zeitverzögert nach ca. 2s angezeigt wird. Das Programm kann man danach mit jeder Taste beenden.

    Das .dotNet Zielframework ist: 4.5.2.

    Was könnte da noch anders als bei dir sein???

    Viele Grüße Fred.

    Mittwoch, 14. Februar 2018 13:44
  • Hi Fred,
    meine Bemerkung bezog sich darauf, dass Du gemeint hast, dass es einen Unterschied zwischen Debug und Release gibt. Diesen Unterschied kann ich nicht nachweisen.

    Bezüglich Deines letzten Posts ist alles ok:

    1. Programm wird asynchron gestartet und meldet "Programm gestartet".

    2. Hauptprogramm wartet auf Zuweisung von Result

    3. Mit SetException wird dem Hauptprogramm mitgeteilt, dass es sinnlos ist, wegen Fehler auf das Ergebnis zu warten und das Exception-Objekt wird der Anweisung durchgereicht, die auf das Resultat warten. Damit wird in den Catch-Zweig des Hauptprogrammes gesprungen.

    4. Das asynchrone Programm läuft danach aber weiter, um beispielsweise aufzuräumen.

    5. Das Hauptprogramm wartet danach nicht auf eine erneute Meldung (Exception oder Result) vom asynchronen Programm, sondern läuft weiter, im konkreten Beispiel bis zu "Fertig, weiter mit beliebiger Taste".


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 14. Februar 2018 14:43
  • Hallo Peter!

    Genau der Punkt 4 ist aber das Problem!

    Das asynchrone Programm, dass aufgerufen wird, soll ja nicht (ganz normal) weiter ausgeführt werden, wenn eine Exception geworfen wurde!!!

    Im konkretem Fall wird im asynchronen Programm eine DB-Abfrage ausgeführt. Wenn jetzt die Datenbank-Anmeldung fehlschlägt, kann ja nicht einfach weiter gegangen werden und die Datenabfrage  ohne eine DB-Verbindung gestartet werden.

    Deshalb die Frage: Wie kann ich im Fehlerfall die weitere Ausführung des asynchronen Programms stoppen?

    Mittwoch, 14. Februar 2018 15:09
  • Hi,
    der Programmierer kann doch das Programm so gestalten, dass nach dem Werfen des Fehler nichts mehr gemacht wird. Wer zwingt Dich nach SetException noch weitere Dinge zu machen? Üblicherweise wird der gesamtes asynchrone Code in Try-Catch eingebaut und SetException lediglich in catch-Block ausgeführt. Zu beachten dabei ist aber, dass nach dem SetException u.U. noch aufzuräumen ist, z.B. Datenbankverbindung schließen. Das kann man über using oder im finally machen. In beiden Fällen werden diesbezügliche Arbeiten auch nach dem SetException ausgeführt.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert perlfred Donnerstag, 15. Februar 2018 08:06
    Mittwoch, 14. Februar 2018 16:52
  • Hallo Peter!

    Da unterscheiden sich synchron und asynchron doch schon ganz erheblich.

    Nach deiner Erklärung habe ich den Ansatz aber verstanden (SetException in catch-Block).

    51 Zeilen in try einzuschließen habe ich sonst immer vermieden.

    Jetzt funktioniert die Exception-Weiterleitung perfekt:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program34
        {
            static void Main(string[] args)
            {
                try
                {
                    Demo c = new Demo();
                    c.Execute();
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
                Console.WriteLine("Fertig, Abschluss mit beliebiger Taste");
                Console.ReadKey();
            }
    
            class Demo
            {
                internal void Execute()
                {
                    TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
                    Task<int> t1 = tcs1.Task;
    
                    // Starten des asynchronen Hintergrund-Threads, der Fehler auslöst
                    Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            Console.WriteLine("\nProgramm gestartet.");
                            Thread.Sleep(2000);
                            throw new Exception("Simulierter Fehler in der async Methode");   // Exception auslösen
                        }
                        catch (Exception ex)
                        {
                            tcs1.SetException(new InvalidOperationException(ex.Message));
                        }
                        finally
                        {
                            Thread.Sleep(500);
                            Console.WriteLine("\n\n!!! finally wird ausgeführt!!!");
                        }
                    });
    
                    // Ergebnis abwarten
                    try
                    {
                        int result = t1.Result;
                        Console.WriteLine("\nErgebnis erhalten.");
                    }
                    catch (AggregateException e)
                    {
                        Console.WriteLine("\nFehler aufgetreten:");
                        Exception ex2 = e;
                        while (ex2 != null)
                        {
                            Console.WriteLine("\n-------------------------------------------------\n{0}", ex2.ToString());
                            ex2 = ex2.InnerException;
                        }
                    }
                }
            }
        }
    }

    Vielen Dank für deine unermüdliche Hilfe!!!

    Fred.





    • Bearbeitet perlfred Donnerstag, 15. Februar 2018 08:21
    Donnerstag, 15. Februar 2018 08:03
  • Hi Fred,
    das mit dem try-catch-finnaly klappt natürlich nur, wenn alle Details in einer Methode ablaufen. Wenn das etwas komplexer ist und eine Klasse genutzt wird, kann auch ein Dispose für Aufräumarbeiten erforderlich werden.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Donnerstag, 15. Februar 2018 10:16