Timer ist aktiv, löst aber nicht aus

  • VB.NET
  • .NET 5–6

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Bluespide.

    Timer ist aktiv, löst aber nicht aus

    Hallo,

    ich habe ein seltsames Problem. Ich habe eine Liste an Dateien, die ich durchlaufe. Dieses durchlaufen der Dateien wird durch einen Timer ausgelöst, der deaktiviert wird, sobald ein bestimmter Dateitype im Verzeichnis gefunden wird.
    Nun wird ein Thread erzeugt, der diese Datei verarbeitet und danach aus dem Verzeichnis entfernt. Sobald der Thread fertig ist, wird der Timer wieder aktiviert und sollte eigentlich nach der Auslösezeit den obigen Prozess immer wieder anstoßen. Tut er aber nicht. Der Timer steht auf enabled, aber er wird nie wieder ausgelöst. Warum?

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class Form1
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Me.Timer1.Enabled = True
    5. End Sub
    6. Public Sub Verzeichnisdurchlaufen()
    7. Dim sPath As String = "v:\DE\outgoing\"
    8. Dim oDir As New System.IO.DirectoryInfo(sPath)
    9. Dim oFiles As System.IO.FileInfo() = oDir.GetFiles("*.txt") 'Nur Textdateien
    10. For Each Dateiname In oFiles
    11. If InStr(Dateiname.ToString, "export", CompareMethod.Text) Then
    12. Timer1.Enabled = False
    13. Dateiverarbeiten(IO.Path.GetFileName(Dateiname.ToString))
    14. End If
    15. Next
    16. End Sub
    17. Public Sub Dateiverarbeiten(Dateiname As String)
    18. Dim ThreadDE As Thread = New Thread(Sub(t)
    19. MsgBox("Test") 'Hier wird eigentlich die Verarbeitung aufgerufen...
    20. Timer1.Enabled = True
    21. End Sub) With {
    22. .IsBackground = True
    23. }
    24. ThreadDE.Start()
    25. End Sub
    26. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    27. Verzeichnisdurchlaufen()
    28. End Sub
    29. End Class
    Keine Ahnung welchen Timer du verwendest, aber ich werfe einfach mal "Start", "Restart" und "Stop" in den Raum.
    Du startest den ThreadDE sofort, der den Timer1.Enabled = True stellt. Aber du befindest dich noch in der Schleife. Wenn jetzt noch eine Datei kommt, die export im Namen hat, dann wird der Timer direkt wieder deaktiviert Timer1.Enabled = False, bevor er wieder ticken kann. Du gehst vermutlich davon aus, dass der ThreadDE schon länger laufen wird und die Schleife bis dahin durch ist, aber da kannst du nicht sicher sein. Das musst du im Code sicher stellen.
    @Superuse Du greifst auf den Timer aus einem Nebenthread heraus zu, da müsste eigentlich eine Threadübergreifende exception kommen.
    Du musst in den GUI-Thread invoken. Probier mal dies (getestet):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Threading
    2. Public Class Form1
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Me.Timer1.Enabled = True
    5. End Sub
    6. Public Sub Verzeichnisdurchlaufen()
    7. Dim sPath As String = "D:\Projekte\___History\___Speck\LidCol2017\"
    8. Dim oDir As New IO.DirectoryInfo(sPath)
    9. Dim oFiles As IO.FileInfo() = oDir.GetFiles("*.txt") 'Nur Textdateien
    10. For Each Dateiname In oFiles
    11. If Dateiname.Name.Contains("export") Then
    12. Timer1.Enabled = False
    13. Dateiverarbeiten(IO.Path.GetFileName(Dateiname.ToString))
    14. End If
    15. Next
    16. End Sub
    17. Public Sub Dateiverarbeiten(Dateiname As String)
    18. Dim ThreadDE As Thread = New Thread(Sub(t)
    19. MessageBox.Show("Test") 'Hier wird eigentlich die Verarbeitung aufgerufen...
    20. EnableTimer(True)
    21. End Sub) With {
    22. .IsBackground = True
    23. }
    24. ThreadDE.Start()
    25. End Sub
    26. Sub EnableTimer(enable As Boolean)
    27. If Me.InvokeRequired Then
    28. Dim ac = New Action(Of Boolean)(AddressOf EnableTimer)
    29. Me.Invoke(ac, enable)
    30. Return
    31. End If
    32. Timer1.Enabled = enable
    33. End Sub
    34. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    35. Verzeichnisdurchlaufen()
    36. End Sub
    37. End Class

    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!

    Dksksm schrieb:

    denk ich auch, Me.Timer ist ein Hinweis auf Forms.Timer
    Der wird mit .Stop / .Start bedient

    Ne, das ist nicht das Problem.
    Schaut euch das mal genau an, könnte zu lustigen Effekten führen.
    Das Programm macht für meine Zwecke schon sinn. Das Programm überwacht ein Verzeichnis. Wenn dort Dateien eingestellt werden, werden diese in einzelenen Threads abgearbeitet. Damit eben keine lustigen Effekte entstehen, wird in dem Moment der Timer abgestellt (ich habe das bisher über Timer enable/disable gemacht, da ich das andere nicht kannte. Wenn die Datei dann abgearbeitet ist, wird der Timer wieder neu gestartet, der das Verzeichnis durchläuft. Da ein Thread durchaus auch mal 5 Minuten laufen kann, darf mir da der aktive Timer nicht dazwischen pfuschen. Beim Heimfahren ist mir auch der Gedanke gekommen, das da wohl ein Invoce fehlt (Threading ist für mich auch ein neues Thema). Damit läuft es nun. Danke @RodFromGermany
    Also wenn die ganze Verarbeitung im NebenThread laufen soll, ists keine gute Idee, den WinForms.Timer zu nehmen, der im MainThread läuft (und dann NebenThreads erstellen, und zu synchronisieren versuchen).
    Da sollte man einen Threading.Timer nehmen, oder einen Timers.Timer.
    Die laufen schon im NebenThread, da muss man keine weiteren NebenThreads erstellen.

    Oder halt was Naifu sagt: Wenn es das ist, was du willst: ein Directory überwachen, da gibts schon was für.
    Den Filesystemwatcher kannte ich bisher nicht. Scheint interessant zu sein. Was ich aber nicht entdecken kann ist, ob der auch Dateien, die beim Programmstart in dem Verzeichnis schon vorhanden sind, auch mit abdecken kann.
    Er soll in meinem Fall auf neu erstellte Dokumente reagieren, aber auch auf bereits vorhandene Dokumente.
    Wenn eine neue Datei erscheint, müßte vermutlich auch noch geprüft werden, ob die von einem anderen Programm noch im Zugriff ist (Das Programm, das die Dateie gerade erstellt). Wie macht man das?

    Zudem ist über den Filesystemwatch das Programm wohl auch so lange ausgelastet, bis die Funktion abgeschlossen ist, die ich über den Filesystemwatcher anstoßen. Ich übertrage hier eine Datei an Amazon und muß warten, bis diese dort verarbeitet ist. Das kann durchaus auch mal 10 Minuten dauern. Über einen Thread habe ich das Problem nicht. Der läuft einfach im Hintergrund weiter und ich kann beliebig neue erzeugen, wenn in der gleichen Zeit weitere Dateien dazu kommen.

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

    Du weißt schon mal nicht, was für Verzeichnisse das sind, wo die liegen und viele Dateien es unterm Strich sind. Können ja auch tausende sein auf langsamen Quellen.
    Sowas macht man nicht gerne im Hauptthread.
    Meine Apps schreibe ich so, dass alles was nicht GUI ist, die hauptsächlich auf den Anwender warten soll, bei mir alles Nebenläufig abgearbeitet wird. Funktionen gleich async / await zu bauen erspart mir viel spätere Arbeit und Testerei.
    na, das scheint mir etwas zu leichtfertig empfohlen - soo einfach ist nach meiner Erfahrung Nebenläufigkeit nicht - auch nicht mit Async.
    Aller-Mindestens musst du Vorkehrungen schaffen,
    • dass die Nebenläufigkeit nicht erneut gestartet werden kann, während sie noch aktiv ist
    • dass die Nebenläufigkeit keinen Crash produziert, wenn dass Programm/Form zwischenzeitlich geschlossen wurde.
    Ansonsten muss man Funktionen garnet "gleich async" bauen.
    Eine Funktion kann man leicht auch nachträglich auf Async umrüsten, solange die Methode nicht in alle möglichen anderweitigen Abhängigkeiten verstrickt ist.

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

    Dksksm schrieb:

    Du weißt schon mal nicht, was für Verzeichnisse das sind, wo die liegen und viele Dateien es unterm Strich sind. Können ja auch tausende sein auf langsamen Quellen
    Jo, aber da ist der Nebenthread auch nicht schneller als der Hauptthread. Und für mich sieht es bisher so aus als hätte der Hauptthread sonst keine Aufgabe.

    ErfinderDesRades schrieb:


    Ansonsten muss man Funktionen garnet "gleich async" bauen.


    Müssen muss man gar nichts, ausser sterben. Zudem war async / await auch mehr als Möglichkeit gemeint, es gibt auch andere Wege, ist mir schon klar.
    War ja auch eine reine Meinungsgeschichte, deshalb bin ich überhaupt nur reingegrätscht.
    Wie du schon schreibst, ist es nicht (immer) so einfach mit der Nebenläufigkeit, so gesehen empfinde ich deine Aussage unten "leicht nachträglich auf asyn umrüsten" etwas widersprüchlich.
    Deshalb ja, man muss gut testen und manchmal kann man sich auch gewatlig selbst in den Arsch schießen zum Beispiel mit Paralell.Foreach, wenn man im Ausnahmefall Benutzeraktivitäten benötigt und auf .Net Framework bis 4.8 angewiesen ist.
    Grundsätzlich, und das ist *meine* subjektive Meinung, mag ich "hängende" Anwendungen überhaupt gar nicht, auch dann nicht, wenn ich selbst die GUI sperren muss, weil auf das Ende der Nebenläufigkeit gewartet werden muss. So kann ich aber immerhin Abbruch anbieten (es gibt (wenige) Jobs die brauchen wirklich lange, teils über Stunden je nach Datenaufkommen und wälzen dann dabei einige 100 tsd. Datensätze komplett durch) und auch den Progress zur GUI durchreichen.
    Alles nur meine ganz persönliche Meinung!

    Superuse schrieb:

    Damit läuft es nun.


    Bis es irgendwann einfach einmal nicht funktioniert und du ein nicht reproduzierbares Fehlverhalten hast und die Frage stellst: Nur ganz selten, aber manchmal bleibt mein Programm einfach Stehen und macht nichts mehr, warum?.