Laden...

Cancel Task / Soll immer nur ein Task laufen

Erstellt von BlueSubmarine vor einem Jahr Letzter Beitrag vor einem Jahr 812 Views
B
BlueSubmarine Themenstarter:in
15 Beiträge seit 2016
vor einem Jahr
Cancel Task / Soll immer nur ein Task laufen

Guten abend,

ich habe einen Codeabschnitt der öfters mal ausgeführt wird und der einen Task startet. Da dieser Abschnitt öfters ausgeführt wird würde er jedesmal einen Task mehr starten. Es soll aber immer nur ein Task gleichzeitig laufen. Leider klappt das canceln des alten Tasks nicht wodurch sich die laufenden Tasks anhäufen. Warum? Freue mich über jede Hilfe.

Hier ein Beispiel:


using System;
using System.Threading;
using System.Threading.Tasks;

namespace CancelTaskTest
{
    class App
    {
        private CancellationTokenSource TaskCancel { get; set; } = new CancellationTokenSource();

        public void Run()
        {
            while (true)
            {
                TaskCancel.Cancel();
                TaskCancel = new CancellationTokenSource();

                Task.Run(() =>
                {
                    while (true)
                    {
                        Console.Write("Active!");

                        Thread.Sleep(5000);

                        if (TaskCancel.Token.IsCancellationRequested)
                        {
                            Console.Write("Cancelled!"); // Never gets called, why?
                            return;
                        }
                    }
                }, TaskCancel.Token);

                Thread.Sleep(15000);
            }
        }
    }
}

Mit freundlichen Grüßen

2.078 Beiträge seit 2012
vor einem Jahr

Das Geheimnis ist der CancellationToken - den gibt's nicht ohne Grund 😉
Jeder Task muss seinen Token für sich habe, dein Code prüft aber immer die jeweils neuste CancellationTokenSource und die ist nie abgebrochen, weil sie immer sofort ersetzt wird.

16.806 Beiträge seit 2008
vor einem Jahr

Was soll das denn werden?
Tasks haben so viele umfangreiche Pattern, dass es eigentlich für alle Zwecke eine stabile Lösung gibt und man nichts selbst "wursteln" muss.
Wenn man da selbst was bastelt, was nicht stabil ist, dann gibts sehr schnell sehr komische Effekte; vor allem bei einem Misch-Masch von asynchroner und paralleler Programmierung.

"Es soll nur ein Task laufen" hört sich sehr nach einem Flow / Consumer-Producer-Pattern / was ähnliches an.

6.911 Beiträge seit 2009
vor einem Jahr

Hallo BlueSubmarine,

Es soll aber immer nur ein Task gleichzeitig laufen.

Das lässt sich i.d.R. durch andere Architekturen besser lösen, wie auch Abt erwähnt.
Z.B. mittels Channels lässt sich ein Producer/Consumer sehr effizient und einfach umsetzen.

Sollte die Anforderung dennoch "nur ein Task gleichzeitig" sein, so lässt sich das via eigenem

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

2.078 Beiträge seit 2012
vor einem Jahr

Die Channel würden zwar nur einen Task zulassen, aber weitere Aufgaben würden warten, oder?
Im Test-Code soll aber der jeweils laufende Task abgebrochen werden, daher schließe ich darauf, dass das auch produktiv so sein soll.

Und ein eigener TaskScheduler klingt ziemlich overkill.
Ich würde - wenn der jeweils laufende Task abgebrochen werden soll - bei dem simplen Ansatz aus dem Test-Code mit der CancellationTokenSource bleiben.
Es erfüllt seinen Zweck und ich leicht zu verstehen und potentiell nötige Architektur-Umbauten lassen sich mit einer Abstraktion zumindest offen halten.

Hängt natürlich immer vom konkreten Ziel ab, aber solange die Anforderungen es nicht erzwingen, würde ich auf eher schwierigere Lösungen (viele haben ja schon Schwierigkeiten mit Tasks an sich) verzichten.

16.806 Beiträge seit 2008
vor einem Jahr

Im Test-Code soll aber der jeweils laufende Task abgebrochen werden, daher schließe ich darauf, dass das auch produktiv so sein soll.

Oder es ist ein Fehler im Code 😉 Aber das wissen wir nicht, solange BlueSubmarine das Geheimnis in sich trägt, was das Ziel ist 😉

Hängt natürlich immer vom konkreten Ziel ab, aber solange die Anforderungen es nicht erzwingen, würde ich auf eher schwierigere Lösungen (viele haben ja schon Schwierigkeiten mit Tasks an sich) verzichten.

Lieber eine Lösung, die funtioniert - wofür man evtl etwas lernen muss - als eine instabile Lösung.
Davon abgesehen gibts eine fast fertige Lösung in den Docs: TaskScheduler Class

2.078 Beiträge seit 2012
vor einem Jahr

Oder es ist ein Fehler im Code

Und auch in der Frage?
Die Frage ist ja gerade, warum die vorherigen Tasks nicht abbrechen.
im Umkehrschluss heißt das, die Tasks sollen abbrechen.

Aber natürlich ist das alles ohne mehr Infos nur Spekulation.

Lieber eine Lösung, die funtioniert

Wieso sollte das nicht funktionieren?
Gut, dieser Code hat seine Macken, aber das Konzept an sich sollte funktionieren.

Davon abgesehen gibts eine fast fertige Lösung in den Docs: TaskScheduler Class

Mich stört nicht, dass ich (wenn es mein Projekt wäre) mich da einarbeiten müsste, sondern dass ich das dann auch von meinen Kollegen verlange, sobald sie an der Stelle arbeiten.
Für mich wäre es schon deshalb eine Option, weil es interessant ist, aber wenn später jemand damit arbeitet und nicht so viel Interesse zeigt, ist diese Klasse ein Fehler-Risiko, das man mit einer einfacheren Implementierung reduzieren oder sogar verhindern könnte.

16.806 Beiträge seit 2008
vor einem Jahr

Gut, dieser Code hat seine Macken, aber das Konzept an sich sollte funktionieren.

Das ist ja das Problem: Theorie und Praxis.
Und Tasks sind so super heikel - wie Du schon sagst verstehen viele Leute Tasks nicht - aber man bastelt dann ein eigenes Konzept, dessen Auswirkungen man nicht kennt / sich dann wundert. Das hilft ja niemanden.

Aber hey: wenn wir wissen, was das Ziel ist, kann man sagen ob es schon was fertiges, stabiles bekannt ist.
Solange machts kaum Sinn sich mit mäßigem Kenntnisstand was eigenes zu überlegen, völlig egal obs "funktioniert" oder dann vermutlich mehr Macken hat als auf den ersten Blick ersichtlich.

Mich stört nicht, dass ich (wenn es mein Projekt wäre) mich da einarbeiten müsste, sondern dass ich das dann auch von meinen Kollegen verlange, sobald sie an der Stelle arbeiten.

Das ist ein allgemeines Problem bei jeden Engineering-Beruf/Umgebung.
Ich nehm auch wann immer möglich Rücksicht auf die Mitwirkende am Code und nehme dann - wenn möglich - die einfachste zu verstehende statt "beste" Lösung.
Aber das ist halt je nach Anforderung nicht immer möglich - und "einfach" is relativ. Allein das Verwenden von Standards vereinfacht schon sehr viel.

Gerade bei sowas hier machts ja sinn eine bewährte Praxis zu verwenden; eben damit man "was bekanntes" nimmt, wozu es auch im Zweifel Lektüre gibt, falls der Busfaktor zum Tragen kommt.

B
BlueSubmarine Themenstarter:in
15 Beiträge seit 2016
vor einem Jahr

Danke für eure Antworten. Im nachfolgenden Beispiel hätte ich genau das was ich bräuchte, nur leider läuft der Code nur mit dem .NET Framework und nicht mit .NET 6 oder 7. Wie würde der Code aussehen, wenn er unter .NET 6 oder 7 laufen würde?


using System;
using System.Threading;

namespace ThreadAbortTest
{
    class App
    {
        private Thread DoesSomethingThread { get; set; } = null;

        public void Run()
        {
            while (true)
            {
                if (DoesSomethingThread != null)
                {
                    DoesSomethingThread.Abort();
                }

                DoesSomethingThread = new Thread(() =>
                {
                    while (true)
                    {
                        Console.Write("Active!");

                        Thread.Sleep(5000);
                    }
                });

                DoesSomethingThread.Start();

                Thread.Sleep(15000);
            }
        }
    }
}

16.806 Beiträge seit 2008
vor einem Jahr

Genauso. Aber jetzt sinds Threads und nicht mehr Tasks.
Threads soll man wenn möglich aber nicht verwenden, weil Threads nicht von .NET verwaltet werden, sondern vom Betriebssystem. Daher kann man Threads auch nicht abbrechen, sondern nur abschießen (inkl. Exception).
Seitdem es Tasks gibt, gibts fast keine Anwendungsfälle mehr, in denen man direkt mit Threads arbeiten soll.

Wenn Du qualitative Hilfe willst, dann musst sagen, was das Ziel dieses Konstrukts ist.

B
BlueSubmarine Themenstarter:in
15 Beiträge seit 2016
vor einem Jahr

Naja der Unterschied ist das der Code mit Threads funktioniert, aber nicht mit Tasks. Ich würde dennoch lieber Tasks verwenden, weil Thread.Abort nur im .NET Framework funktioniert und eben eigentlich nicht genutzt werden sollte.

Es handelt sich um ein sehr simples Konstrukt. Es gibt den Hauptthread und eben genau den einen Helferthread. Der Hauptthread teilt sich in 2 Bereiche auf und der Helferthread darf nur in Bereich 2 mitlaufen. Bestimmte Bedingungen in Bereich 2 können dazu führen das der Hauptthread wieder in Bereich 1 übergeht und die ersten Zeilen des Bereich 1 dafür sorgen das der Helferthread gestoppt wird, bis der Hauptthread irgendwann wieder in Bereich 2 übergeht und der Helferthread wieder gestartet wird. Also quasi:

Hauptthread Bereich 1 -> Helferthread gestoppt
Hauptthread Bereich 2 -> Helferthread läuft

16.806 Beiträge seit 2008
vor einem Jahr

Steht ja schon 2-3 mal im Thread; aber beantworte doch mal die Frage nach dem Use Case.
Dann kann man Dir entweder a) sagen, dass es doch ein simples CancellationTokenSource-Konstrukt ist, oder b) Dir einen Pattern nennen, mit dem man den Use Case stabil umsetzen kann.

Weil Deine Beschreibung hört sich nach nem ziemlichen Spaghetti-Workaround an, ohne dass der Sinn klar wird.
Niemand weiß hier, was Du mit "Hauptthread" meinst; viele Anwendungsarten haben keine Hauptthreads, sondern zB vor allem Desktop Anwendungen dessen UI Threads.
Was Du aber beschreibst ist im Endeffekt eine gewisse Art von Logik, die die UI gar nicht haben soll, wenn man sich moderne Architekturmuster anschaut. Auch hat die Beschreibung jetzt nichts mehr mit einem "es darf nur ein Task laufen" zutun.
Also: was soll das Konstrukt inhaltlich tun?