Warum dauert dieser C# Task so lange?

Ryeera  03.03.2021, 21:05

Deine Frage ist etwas verwirrend, weil du immer "synchron" sagst, auch wenn du "asynchron" meinst.

michiwien22 
Fragesteller
 03.03.2021, 21:21

Danke, habe das korrigiert. Hoffe es wird dann bald sichtbar.

1 Antwort

Vom Fragesteller als hilfreich ausgezeichnet

Da warten Tasks aufeinander.

Lass mal vor und nach den Wait/WaitAsync-Aufrufen was in die Konsole schreiben, Du wirst fest stellen: Während Du wartest nicht einfach nur, sondern da werden die ganze Zeit Tasks gestartet.

Irgendwann entsteht eine Situation, die dazu führt, dass diese startenden Tasks auf einen anderen Task warten müssen, soweit ich das erkennen kann, immer sobald der erste asynchrone Task einmal durch ist.
Das wiederum hängt vermutlich mit dem Delay zusammen, wenn Du das raus nimmst, geht's nämlich schnell. Vermutlich soll ein Delay gestartet werden, kommt aber nicht dazu, weil der Scheduler mit einem Stapel anderer Tasks beschäftigt ist.

Wenn aber ein Stück Code auf einen anderen Task wartet (also asynchron arbeitet), kann (hängt vom zweiten Task ab) der Thread, der gerade wartet, wieder freigegeben werden, bis ihn ein anderer Task zugewiesen bekommt.

Abgesehen davon solltest Du dir anschauen, was "ConfigureAwait" wirklich tut und wann und warum man es nutzen sollte. In Konsolenanwendungen ist das z.B. ziemlich nutzlos ;)

Außerdem ist es eher selten eine gute Idee, einfach nur einen Task zu starten. In den allermeisten Fällen läuft ein Task so lange synchron, bis irgendwann ein tatsächlich länger laufender Teil Code kommt. DAS darf dann ein eigener Task sein - oder eine andere Technologie (z.B. TaskCompletionSource) - oder bereits vorhandene asynchrone Implementierungen. Da liegt nämlich der Vorteil von Tasks: Sie können (hängt vom Thread ab) Code aus dem aktuellen Thread an einen anderen Thread übergeben und danach wieder zurück holen, das lohnt sich aber nur, wenn der synchron laufende Teil auch in einem solchen interessanten Thread läuft.

Woher ich das weiß:Berufserfahrung – C#.NET Senior Softwareentwickler
michiwien22 
Fragesteller
 03.03.2021, 23:02

das ist ja nicht ein realer Code, er dient dazu, ein Problem zu demonstrieren, das in einem komplexrn Zusammenhang aufgetreten ist.

0
Palladin007  03.03.2021, 23:04
@michiwien22

Hab ich mir schon gedacht - mir ging's darum, dass dir klar ist, wie wichtig es ist, wirklich im Detail zu verstehen, wie Tasks funktionieren.
Es gibt viele kleine Details, die nebensächlich wirken, aber in Wahrheit weit davon entfernt ist.

1
Palladin007  03.03.2021, 23:10
@michiwien22

Bzw. ich hab's eigentlich für ein Experiment gehalten.

Aber wenn Du ein derartiges reales Problem hast:
TaskCreationOptions.LongRunning umgeht das Problem, der Thread von dem Task landet dann nicht auf dem ThreadPool und blockiert daher auch keinen ThreadPool-Thread. Allerdings muss dann jedes mal ein neuer Thread erzeugt werden.

Aber es gibt sicher bessere Alternativen, die sind dann aber vom konkreten Ziel abhängig.

1
Palladin007  03.03.2021, 23:19
@Palladin007

PS:

LongRunning muss natürlich für den synchron arbeitenden Task eingestellt werden und das geht über die Factory.

1
michiwien22 
Fragesteller
 04.03.2021, 08:15

Habe auch auf Stack-Overflow nachgefragt:

Soweit ich das jetzt im Ansatz verstehe, gehen dem CLR die threads aus, da irgendwann alle verfügbaren threads aufgrund des synchronen Wait blockiert sind und kein freier Thread Kontext zur Verfügung steht. Das erzeugt dann einen Deadlock. Warum geht es nach 16 sekunden weiter?

Das liegt daran, dass die CLR alle 500ms weitere threads zum Thread Pool dazufügt, wenn sie feststellt, dass keine Arbeit erledigt wird, und alles steht. Nach 16 Sekunden habe ich dan wieder genügend threads und es geht weiter. Davon habe ich nichts gewusst...

https://stackoverflow.com/questions/66463878/what-makes-mixing-of-sync-and-async-tasks-so-terrible-slow-in-the-following-exam

0
michiwien22 
Fragesteller
 04.03.2021, 08:32

Wenn es keine Limitierung in der Anzahl der Threads gäbe (siehe Kommentar von Theodor im SO Beitrag), dann träte das Problem gar nicht auf. Rein konzeptionell (unter der Annahme, dass unendliche Ressourcen bereit stehen) habe ich hier keinen Deadlock, da der Task, der den Semaphor gelockt hat und auf das Ergebnis von Task.Delay() wartet ja weitermachen könnte, sobald das Ergebnis da ist. Scheinbar ist es aber so, dass dann kein Thread mehr zur Verfügung steht, wo der Kontext weiterlaufen könnte.

Sowas ist daher extrem gefährlich und man muss zumindest ein ganz kleines bisschen darüber Bescheid wissen, was im Hintergrund passiert.

Klarerweise passiert das nicht, wenn man asynchrone Aufrufe für den Semaphor verwendet. Anfängerfehler. ich beschäftige mich erst seit knapp zwei Monaten mit C# und da ist so ein peinlicher Schnitzer hoffentlich vertretbar.

0
Palladin007  04.03.2021, 09:47
@michiwien22
Das liegt daran, dass die CLR alle 500ms weitere threads zum Thread Pool dazufügt, wenn sie feststellt, dass keine Arbeit erledigt wird, und alles steht. Nach 16 Sekunden habe ich dan wieder genügend threads und es geht weiter. Davon habe ich nichts gewusst...

Das wusste ich auch noch nicht - das war der kleine aber feine Punkt, den ich noch nicht erklären konnte.
Wieder was gelernt :D

Ich würde aber dennoch auf LongRunning zurück greifen, damit kannst Du nämlich genau steuern, welcher Task über dem ThreadPool läuft und welcher nicht. Schau dir dazu Task.Factory an, da hast Du massig Methoden mit vielen verschiedenen Möglichkeiten.

Aber ja:
Ein ThreadPool-Task sollte nur möglichst kurz aktiv sein. Der Trick bei async/await ist ja auch, dass ein await eigentlich die ganze Zeit rein synchron arbeitet und den aufrufenden Thread nie verlässt. Erst wenn das await vor einem Task steht, der tatsächlich einen neuen Thread haben will, erst dann gibt es einen Kontext-Wechsel zum neuen Thread.
Das heißt, dass lang laufender synchroner Code nicht asynchron arbeitet, nur weil die Methode async ist und den aktuellen Kontext blockiert.
Das heißt aber auch, dass längere Aufgaben mit relativ wenig Komplexität aus dem aktuellen Thread heraus gezogen werden können, das ist besonders bei WinForms/WPF nützlich.
Man erkauft sich eine ganze Reihe von Vorteilen mit deutlich mehr Detail-Komplexität, die schwer zu verstehen ist.

Beachte aber auch, dass der Code nach dem await nicht unbedingt eim neuen Thread läuft. Normalerweise schon, außer wenn es einen SynchronizationContext gibt, der den nächsten Code wieder in den vorherigen Thread synchronisieren kann - bei WinForms und WPF ist das der Fall.
Schau dir mal mit Konsole und WinForms oder WPF Tasks und einander aufrufende asynchrone Methoden im Allgemeinen an und lass dir bei jeder Gelegenheit die aktuelle Thread-ID mit loggen, das überrascht viele.

ich beschäftige mich erst seit knapp zwei Monaten mit C# und da ist so ein peinlicher Schnitzer hoffentlich vertretbar.

Das ist definitiv vertretbar. Tasks/async/await sind genial und eröffnen viele Möglichkeiten, aber das ganze Thema ist schwer zu durchblicken, da viel im Hintergrund passiert und Thread-Synchronisation generell ein sehr schweres Thema ist.

Aber Du kannst für dich mit nehmen, dass es ungewöhnlich ist, dass jemand Neues so sehr hinter dem Verständnis von Zusammenhängen hinterher ist. Das ist (besonders bei so Themen) sehr wichtig, aber leider nicht selbstverständlich und ich würde es mir dieses Detail-Interesse bei meinen Kollegen auch wünschen.

PS:

Freundschaftsanfragen lehne ich ab - steht aber auch in meinem Profil.
Stell deine Frage hier oder auf mycsharp.de <- Ist definitiv zu empfehlen, setzt aber viel Eigeninitiative voraus.

1
michiwien22 
Fragesteller
 04.03.2021, 10:23
@Palladin007

>Stell deine Frage hier oder auf mycsharp.de

Ich gehe nur sehr ungern auf solche Foren, da man dort als Anfänger in den allermeisten Fällen runtergemacht wird, als wäre man ein Idiot (mikrocontroller.net wäre ein Paradebeispiel - dort sind zu 95% Psychopathen am werk, die nur darauf warten, einen anfänger runterzumachen). Man kann schon fast sicher sein, dass man, wenn man eine Frage nicht auf allerhöchstem Expertenniveau stellt, einen Downgrade oder wüste Beschimpfungen empfängt. Deshalb mache ich das auch nicht mehr. Bei Stackoverflow sind die Leute wenigstens sachlich, auch wenn man Anfängerfragen stellt. deshalb hab ich mich dort auch hingetraut.

0
Palladin007  04.03.2021, 10:26
@michiwien22

Das meinte ich mit viel Eigeninitiative.
Man sollte also die Grundlagen drauf haben, doch dafür gibt's (kostenlose) Bücher und andere Quellen.

Gleichzeitig lernt man da aber auch eine gewisse Art zu arbeiten, die auch anderswo sehr nützlich sein kann.

Deine Frage wäre (vermutlich) auch sachlich behandelt worden - wenn Du dazu geschrieben hättest, dass das ein Experiment bzw. ein minimalisiertes konkretes Problem ist :D

0
michiwien22 
Fragesteller
 04.03.2021, 10:50
@Palladin007

>Deine Frage wäre (vermutlich) auch sachlich behandelt worden

Naja, immerhin hast DU ja sachlich geantwortet.

>Man sollte also die Grundlagen drauf haben,

bis ich die Grundlagen drauf habe, wird es wohl noch ein wenig dauern... C# ist intern scheinbar recht komplex. Das mit den Tasks/SynchronzationContext habe ich bisher noch nie so richtig verstanden, obwohl mir dieses Buch half:

https://www.amazon.com/Async-5-0-Unleash-Power/dp/1449337163

Kennst du andere Bücher, wo man das lernen kann?

0
Palladin007  04.03.2021, 11:46
@michiwien22
Naja, immerhin hast DU ja sachlich geantwortet.

Ich bin da auch aktiv :D Nicht so sehr, wie Andere, aber trotzdem.

C# ist intern scheinbar recht komplex
Das mit den Tasks/SynchronzationContext habe ich bisher noch nie so richtig verstanden

Das stimmt - solche Internas würde aber vermutlich niemand als Grundlagen bezeichnen. Ich würde aber die korrekte Nutzung von async/await als Grundlage bezeichnen, also z.B. so Details, dass man möglichst kein Task.Wait() nutzen sollte. Wie das im Hintergrund funktioniert, muss aber kein Anfänger schon wissen.

Kennst du andere Bücher, wo man das lernen kann?

[FAQ] Wie finde ich den Einstieg in C#? | myCSharp.de

Da ist auch das kostenlose Openbook von Andreas Kühnel verlinkt. Das finde ich persönlich sehr gut und gibt dir alles mit, was Du brauchst, um eigenständig Anwendungen zu entwickeln. Natürlich ist da nicht alles mit dabei, da es ja schon 9 Jahre alt ist, doch der Inhalt bleibt weiterhin gültig.
Ich kenne allerdings nur die 2010er Version, die habe ich damals gelesen.

Besser wäre natürlich die 2019er Version, doch die gibt's nicht kostenlos.
Darüber hinaus ist die MSDN-Doku von Microsoft sehr zu empfehlen, allerdings ist die ziemlich umfangreich.

Ansonsten gibt's da seit einiger Zeit auch den Anfänger-Bereich, doch so Fragen wie "Wie geht das?" oder "Wie muss ich das schreiben?" werden auch da ziemlich schnell abgeschmettert. Ein bisschen Eigeninitiative ist wichtig und das meint: Suchmaschine nutzen, MSDN-Doku durchsuchen, Forum durchsuchen. Findet man da nix, darf man im Forum fragen.

0