Task wechselt Thread mittendrin

  • VB.NET
  • .NET 5–6

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    Task wechselt Thread mittendrin

    Hallo,

    hier ein kleines Phänomen, das ich nicht verstehe.

    VB.NET-Quellcode

    1. Sub New() 'MainForm
    2. InitializeComponent()
    3. ...
    4. Task.Run(AddressOf TidyUpFolders)
    5. End Sub
    6. Private Sub TidyUpFolders()
    7. CWrite("Cleaning Folders") ''Funktioniert
    8. ... (benötigt ein paar Sekunden)
    9. CWrite("Cleaning finished") ''Threadübergreifender Vorgang
    10. End Sub

    CWrite schreibt dabei unter anderem Text in eine RichTextBox die sich auf dem MainForm befindet.
    Ohne das als Task zu verpacken, lädt das MainForm natürlich nicht bis die Methode durch ist. Ich habe aber erwartet, dass mir "threadübergreifender Vorgang" schon beim ersten Aufruf von CWrite() angezeigt wird.
    Eine Idee wieso ich die rtb erstmal noch beschreiben kann?

    Und kurioser Weise, im Release, läuft sogar die zweite CWrite noch ohne Probleme.
    Im Release gibts also keine zwei Threads?

    zur Reproduktion reicht das schon

    VB.NET-Quellcode

    1. Sub New()
    2. InitializeComponent()
    3. Task.Run(AddressOf Test)
    4. End Sub
    5. Private Sub Test()
    6. Textbox1.Text = "A"
    7. Threading.Thread.Sleep(1000)
    8. Textbox1.Text = "B"
    9. End Sub


    Viele Grüße

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Das erste .Text = "A" scheint noch mit dem GUI-Thread mitzulaufen, durch das Sleep wird es wohl aber aus der Bahn geworfen. Aber zu den Hintergründen/Details weiß ich noch nix.

    Haudruferzappeltnoch schrieb:

    Im Release gibts also keine zwei Threads?
    Doch, aber im Release werden keine Exceptions geworfen/angezeigt.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Der Aufruf von

    Haudruferzappeltnoch schrieb:

    VB.NET-Quellcode

    1. Task.Run(AddressOf Test)
    im Konstruktor ist mir sehr suspekt.
    Pack das mal in eine Button_Click-Prozedur.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Die Anwendung muss autonom laufen können, daher ist ein Button nicht gut, aber der Gedanke nicht schlecht. Hab es jetzt mit einem Timer versetzt gestartet.

    VB.NET-Quellcode

    1. Private Sub timeSetup_Tick(sender As Object, e As EventArgs) Handles timeSetup.Tick
    2. timeSetup.Stop
    3. Task.Run(AddressOf Test)
    4. End Sub
    5. Private Sub Test()
    6. Textbox1.Text = "A"
    7. Threading.Thread.Sleep(1000)
    8. Textbox1.Text = "B"
    9. End Sub


    Nun haut das erste Schreiben schon nicht mehr hin, wie eigentlich gedacht.

    Aber die exen laufen weiterhin ohne Probleme ?( (Für Spaß, Button hab ich auch probiert, selbes Ergebnis)

    Warum ist das in der Sub New suspekt?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Der Unterschied scheint nicht zwischen Release und Debug zu liegen, sondern im aufrufenden Prozess.
    Argh, wer lesen kann. Ich hab nen Unterschied zwischen Start per Studio und Ausführung der EXE gesehen. Wenn ich die pure EXE mit .NET Framework 4.8 starte, kann ich auch Throw New ArgumentException drin haben und das wird ignoriert.

    Haudruferzappeltnoch schrieb:

    Das Studio in beiden Fällen nicht.
    Kann ich nicht mit Sub New bestätigen, da wird A reingeschrieben (Debug, egal, ob mit VS oder pur EXE), siehe Anhang.
    Bilder
    • Crash.gif

      350,19 kB, 904×596, 71 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    In eigenständiger EXE m MainForm-Konstruktor:
    • Exception im GUI-Thread -> Programm wird beendet
    • Exception im Nebenthread -> Thread wird beendet, Programm läuft weiter (in fragwürdigem Zustand)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ahja sry. Exception im Nebenthread ist natürlich großer Murks...

    CurrentManageThreadId sagt, wenn Task.Run in der Sub New steht:
    In der Sub New vor dem Task.Run: 1
    In der Sub New nach dem Task.Run: 1
    Beim A schreiben: 6(oder 4 oder 9 vermutlich noch andere)
    Beim B schreiben: 6(oder 4 oder 9 vermutlich noch andere)

    vermutlich geht die Exception beim A schreiben beim Debuggen irgendwie unter.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Warum ist das in der Sub New suspekt?


    Weil das der Constructor ist.
    Der Constructor hat nur eine Aufgabe: dein Objekt initialisieren. Da kommt maximal Logik rein, die du fürs Initialisieren deiner Komponenten/deines Objektes brauchst - InitComponents() zum Beispiel. Es macht nichts anderes, als deine UI-Komponenten einzurichten.

    Mal abgesehen davon, dass Logik im Constructor dafür sorgen kann, dass die Objektinitialisierung (zu) lange dauert, wenn Logik drin ist, kannst du dich eben nicht drauf verlassen, dass bei einer Exception (oder einem anderen Fehlerfall) dein Objekt so initialisiert ist, wie du es erwartest, was zu Folgefehlern führen kann.

    Wenn sowas autonom vonstatten gehen soll, dann nutze ein Event, oder ein zeitgesteuertes Ereignis.
    Event wäre z.B. Form_Loaded. Ein zeitgesteuertes Ereignis wäre dann, dass du in dem Aufrufer nach Objekt-Init nochmals eine Init()-Methode aufrufst, oder ein Thread erstellst, was dann irgendwann definitiv nach Initialisierung deines Objekts etwas tut.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.