Countdown (Threading)

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 56 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Countdown (Threading)

    Hallo,

    ich habe eine Frage bezüglich Threading. Ich hab ein UI, worauf ich über einen Click auf einen Button nun einen Thread starte (dieser soll von einer x-beliebigen Zahl runterzählen). Nun muss aber zeitgleich auf der UI ein Label angepasst werden welches die aktuelle verbleibende Zeit anzeigt. Soweit okay. Nun bekomme ich aber einen Fehler wenn ich einfach in meinem Thread das Label verändern will:

    Ein Ausnahmefehler des Typs "System.InvalidOperationException" ist in WindowsBase.dll aufgetreten.

    Zusätzliche Informationen: Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet.


    Nun habe ich mir dazu über Google ein paar Tutorials angeschaut/durchgelesen & bin ehrlich gesagt immernoch nicht dahinter gestiegen wie ich es nun schaffe diesen Text zu ändern. Mir sind Begriffe wie "Invoke" hängen geblieben, was mir jedoch auf dem Weg zur Problemlösung nicht wirklich weitergeholfen hat.

    Kann mir von euch das jemand näherbringen? Oder vielleicht einen Beispiel-Code posten wo das Problem behandelt wird?


    Würde mich echt freuen.
    Unsauber:
    CheckForIllegalCrossThreadCalls = False

    Sauber:

    VB.NET-Quellcode

    1. Private Delegate Sub ChangeLabelTextDelegate()
    2. Public Shared Sub ChangeLabelText()
    3. Dim del As New ChangeLabelTextDelegate(AddressOf ChangeLabelText)
    4. theControl.Invoke(del)
    5. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „xd-franky-5“ ()

    Schrubbii schrieb:

    Mir sind Begriffe wie "Invoke" hängen geblieben
    Such mal im Forum nach Invoke, da wirst Du über die Lösung gestolpert.

    VB.NET-Quellcode

    1. Me.Invoke(Sub() Me.Label1.Text = "Bla")
    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!
    dann mach den VerarbeitungsProzess im NebenThread.

    Aber ich glaub, deine Anforderung ist noch anders gelagert, also es geht vmtl. weniger um einen CountDown als um eine Prozess-Fortschritts-Anzeige.
    Und in Wpf gibts kein Control.BeginInvoke, da würdeman - entweder mittm Dispatcher/DispatcherTimer rumwursteln oder was gscheits bauen mittm Async-Await-Pattern.

    Allerdings sollte überhaupt erstmal der MVVM-Pattern Anwendung finden - ist das bei dir gegeben?
    Hi @Schrubbii!

    Also ich hab mir auch schon mal so einen Countdown gemacht. Aber, wie EdR es bereits gesagt hat, mit einem Timer (und 3 NumericUpDowns) ;D

    Dafür hab ich die Teile nudH, nudM und nudS genannt, den Timer auf 1000 ms gestellt
    Ich lass zuerst den Benutzer die Zeit einstellen, die runter gezählt wird, aus den Ständen berechnet ich mir dann die Sekunden. Falls die "Zähler" auf 0 stehen, also 0 sekunden eingegeben wurden, wird einfach 23h 59m 59s angenommen. Im Timer-Tick-Event werf ich die Werte aus den NumericUpdowns in 3 Integer variablen (trenne GUI und Code) und dekrementiere die Variablen in einer If...Then... Else If...Else...End If Falluntescheidung und setze die NUDs dementsprechend.

    Was halt noch wichtig ist, etwa die NUDs dürfen nur positive Zahlen haben(also als Minimalwert 0 einstellen und als Maximalwert halt 23 (für Stunde) bzw 59 (für Sekunden und Minuten)

    Lg Radinator

    PS:Also ich geb zu, das schwerste an dem Timer ist die If Then ElseIf Else End If Konstruktion, hab mir aber mir einer WerteTabelle und Excel den Code gut erarbeiten können ;D
    Musst nur überlegen, was passieren soll, wenn 04:00:00 und es runter zählt, welche werte du dann wo setzen musst und unter welchen Zuständen welches NUD, welchen Wert haben soll/darf/kann.
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    @Radinator
    Bitte sag mir, dass du nicht der Meinung bist, dass du es so programmiertechnisch korrekt gemacht hast...

    @Schrubbii
    Nutzt du denn MVVM oder machst du alles direkt im CodeBehind des Windows?
    @ErfinderDesRades:
    Nein, MVVM ist (aktuell) nicht vorhanden. Kommt sobald alles soweit programmiertechnisch fertig ist.


    @Radinator:
    Okay - ich hab den Countdown jetzt mehr oder weniger so gelöst dass ich über den Input alles in Sekunden habe & dies über folgende Funktion als hh:mm:ss ausgebe:

    VB.NET-Quellcode

    1. Private Function formatCountdown(value As Integer) As String
    2. Dim hoursLabel As Integer = 0
    3. Dim minutesLabel As Integer = 0
    4. Dim secondsLabel As Integer = 0
    5. hoursLabel = value \ 3600
    6. secondsLabel = value Mod 3600
    7. minutesLabel = value \ 60 - (hoursLabel * 60)
    8. Return Format(hoursLabel, "00") & ":" & Format(minutesLabel, "00") & ":" & Format(secondsLabel, "00")
    9. End Function


    Für den parallelen Countdown an sich hab ich einen Thread erstellt welcher erstmals seine vorherige Zeit in MS nimmt & diese jeden Durchlauf (einmal alle 1000MS) überprüft. Ist die Abweichung zur vorherigen Zeit zB 1020ms anstatt 1000, wird der nächste "Sleep" nur 980ms gesetzt. Aktuelles Problem ist nun dass ich über den Funktionsaufruf meiner Verarbeitung nun folgendes Problem habe: Ich hab Thread2 & rufe dort eine Funktion meiner Form-Thread auf. Nun benötigt dieser aber auch Berechtigungen in diesen einzugreifen.. Hab bisher noch nicht rausgefunden wie ich mit Thread2 dem Thread1 sage dass er Funktion XYZ starten soll... Alles kniffelig.



    @nafets:
    "noch" ist alles in einer Klasse hinterm Window. Sobald das Programm aber programmiertechnisch steht, wird es von mir nochmal nach dem ModelView-Konzept gepackt. Bin halt erst seit 6 Tagen ca. an VB.net überhaupt dran & musste mir erst alles beibringen. Fand das da relativ "einfacher" alles erstmal in die MainClass zu schreiben. Hab aktuell ein weiteres Problem (Timer ist abgeschlossen), siehe dazu den letzten Text-Brocken des obrigen Textes welcher an Radinator ging.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Schrubbii“ ()

    Sorry - hat sich 1ne Zeile verflüchtigt...

    VB.NET-Quellcode

    1. Private Function formatCountdown(value As Integer) As String
    2. Dim hoursLabel As Integer = 0
    3. Dim minutesLabel As Integer = 0
    4. Dim secondsLabel As Integer = 0
    5. hoursLabel = value \ 3600
    6. secondsLabel = value Mod 3600
    7. minutesLabel = value \ 60 - (hoursLabel * 60)
    8. secondsLabel = value Mod 60
    9. Return Format(hoursLabel, "00") & ":" & Format(minutesLabel, "00") & ":" & Format(secondsLabel, "00")
    10. End Function
    ... das war jetzt aber viel. Alle Casts von CInt usw. abgeändert, Format ebenfalls. Nun ist jedoch immernoch das Problem dass ich aus meinem 2. Thread nicht den Prozess zur Verarbeitung laden kann. Wenn ich den Methoden-Aufruf ebenfalls über die Invoke-Variante mache müsste ich jede Variable & jede Methode abändern - was eher unvorteilhaft wäre.

    Gibt es ne Möglichkeit mit Thread2 dem MainThread zu sagen "Führe doch bitte Methode xyz aus"?
    so, und nu schau mal dieses an:

    VB.NET-Quellcode

    1. Private Function formatCountdown(totalSeconds As Integer) As String
    2. Return TimeSpan.FromSeconds(totalSeconds).ToString("hh\:mm\:ss")
    3. End Function
    Die Lösung - zeile#2 - ist so kurz und selbsterklärend, das auf eine Extra-Funktion drumrum sogar verzichtet werden sollte.

    Das illustriert glaub auch ziemlich eindrücklich, was für ein Schrott die Vb6-Format-Funktion ist.
    Und illustriert auch, wie der Deppen-Namespace bewirkt, dass man nicht im Framework nach den überlegenen Methoden recherchiert, sondern sich eher mit dem vb6-Müll iwas zusammenbastelt, weil einem das ständig unter die Tastatur kommt.

    Deshalb ist beides so wichtig: Den Deppen-Namespace abklemmen, und Option Strict On machen.
    Das bewirkt, dass man sich kenntnismäßig kontinuirlich weiterentwickelt, indem man sichs zum Prinzip macht, die Datentypen für sich arbeiten zu lassen - dafür sind sie ja da.
    Aber dafür muss man sie halt auch wahrnehmen, und muss man sie auch immer wieder, und immer weiter recherchieren.

    Zum Recherchieren nehme ich im ersten Schritt immer den ObjectBrowser, der hat sowohl eine Kontext-Hilfe als auch eine Suche per Suchbegriff.
    Meist ist das ausreichend, aber wenns mal nicht hinreicht konsultiere ich auch MSDN.
    Etwa für die richtige FormatString-Angabe beim Timespan habich auf MSDN nachgucken müssen.
    MSDN hat leider keine brauchbare Suche per Suchbegriff, also da muss man dann googeln, und die MSDN-Treffer wählen, um hofflich zu verlässlichen Aussagen der Referenz-Doku zu kommen.
    Allerdings sind die MSDN-Beispiele oft auch ziemlicher Müll, und so können gelegentlich auch ergoogelte Community-Treffer richtige Vorgehensweisen demonstrieren, wo bei MSDN nur absolut Schrulle steht.
    (Die MSDN-Codesamples zur Timespan-Formatzeichenfolge aber sind übrigens hervorragend, nur leider schwer zu finden)

    Beide Recherche-Methoden (ObjectBrowser, MSDN) sind hier im Video gezeigt: VisualStudio richtig nutzen (Google ist nicht deine Mami)

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