ich habe ein seltsames Problem (bzw. zwei Probleme) mit Threads festgestellt, was mir bereits sehr viele Nerven gekostet hat, bis ich auf die Ursache kam.
Das Problem tritt bei mir unter Windows 10 x64 (andere OS nicht getestet) mit Version 5.71 und der neuen 5.73 auf (andere Versionen nicht getestet), sowie bei x32 und x64.
Es macht auch keinen Unterschied, ob man Thread-Sicher kompiliert oder nicht.
Problembeschreibung:
Einen Thread sollte man vernünftig über eine globale Variable beenden, damit der Thread selbstständig (z.B. per ProcedureReturn) ausläuft.
Jetzt hatte ich den Fall, eine Timer-Routine in einem Thread zu implementieren...die Zeit X, nach der diese Routine beendet werden soll, wird direkt aus einem SpinGadget herausgelesen.
Alleine die Tatsache, dass ich den Wert des SpinGadgets im Thread auslese, führt dazu, dass der Thread zwar einwandfrei läuft, sich jedoch nicht beenden kann (seht ihr z.B. am nicht umschaltenden OptionGadget). Ändere ich den Wert X
auf eine konstante Zahl, so beendet sich der Thread wie erwartet.
Dazu habe ich mal ein kleines Testprogramm erstellt, damit ihr den Bug reproduzieren könnt.
In der Event-Routine habe ich zwei mögliche Warte-Routinen eingetragen, die das Ende des Threads abwarten. Einmal per WaitThread (das Programm würde einfrieren, wenn man kein TimeOut setzt) und einmal eine selbstgeschriebene Routine, bei der das Programm ebenfalls hängen bleibt, bis nicht der TimeOut abgelaufen ist.
Durch Rätseln und Probieren habe ich bereits eine Lösung für das Problem gefunden: Setzt man ein einfaches WindowEvent() in die Timer-Routine, so wird der Thread ordnungsgemäß beendet (warum auch immer).
Zweites Problem:
Der Thread sollte alle 100ms ins Debug Fenster schreiben, bis der TimeOut (spinGadget-Text) abgelaufen ist. Danach gibts 1000 ms Pause, bis das ganze von Vorne beginnt.
Aus irgendeinem Grund, werden zwischen den 100ms jeweils zwei gleichzeitige Schreibvorgänge ins Debug-Fenster durchgeführt. Das darf laut Code gar nicht sein.
Hier das Test-Programm:
Code: Alles auswählen
EnableExplicit
Global EnableThread.l = 0
Global ThreadID.i
Global frmTestWindow.l = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 200, 150, "Thread-Bug Test")
Global optEnableThread.l = OptionGadget(#PB_Any, 40, 20, 100, 25, "Enable Thread")
Global optDisableThread.l = OptionGadget(#PB_Any, 40, 40, 100, 25, "Disable Thread")
Global spinTest.l = SpinGadget(#PB_Any, 40, 80, 100, 25, 0, 100)
SetGadgetText(spinTest, "5")
Procedure TestThread(*Value)
Repeat
Define Timer.q = ElapsedMilliseconds()
Repeat
If Not EnableThread
ProcedureReturn
EndIf
Debug "Thread-Output"
Delay(100)
Until ElapsedMilliseconds() - Timer >= Val(GetGadgetText(spinTest)) * 1000 ; <- the reading of the spin-gadget creates the problem that the thread does not end! Why?
; Until ElapsedMilliseconds() - Timer >= 5000 ; <- No problems with a constant number
Debug "Pause"
Delay(1000)
ForEver
EndProcedure
Define Event.l
Repeat
Event = WaitWindowEvent()
If EventWindow() = frmTestWindow
If Event = #PB_Event_CloseWindow
CloseWindow(frmTestWindow)
End
EndIf
If EventGadget() = optEnableThread
EnableThread = 1
ThreadID = CreateThread(@TestThread(), 0)
EndIf
If EventGadget() = optDisableThread
EnableThread = 0
; If IsThread(ThreadID)
; WaitThread(ThreadID, 2000) ; <- This freezes...resume only after time out!
; EndIf
Define Timer.q = ElapsedMilliseconds()
Repeat
If Not IsThread(ThreadID) : Break : EndIf
Delay(100)
; WaitWindowEvent() ; <- This command solve the freezing-problem!!! Why???
Until ElapsedMilliseconds() - Timer >= 2000 ; <- This freezes also...resume only after timeout of 2000 ms. Or no problem with WaitWindowEvent()-Command
EndIf
EndIf
ForEver
Vielen Dank für die Unterstützung und viele Grüße,
Andi