Threadkommunikation zur Timersteuerung

  • VB.NET

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von shell.

    Threadkommunikation zur Timersteuerung

    Moin,

    ich tueftel gerade nen bisschen an nem Tool rum, eine Aufgabe ist es 2 Timer (basierend auf einer Klasse) am Laufen zu halten, diese beiden Timer sind voneinander abhaengig in der Aktion die nach ablaufen der Periode ausgeführt wird - zwingend.

    Vorab die Klassen:

    VB.NET-Quellcode

    1. Public Class clsTimer
    2. ' Timer
    3. Private WithEvents _eventTimer As New Timer
    4. ' Variables
    5. Private eventTimerTicks As Double = 1
    6. Private eventTimerNeeded As Double = 0
    7. Private eventTimerName As String = Nothing
    8. Private eventTimerMinutes As String = "0"
    9. Private eventProgressBar As ProgressBar
    10. Private timeMax As String = Nothing
    11. Private timeDone As String = Nothing
    12. ' Events
    13. Public Event startedTimer()
    14. Public Event stoppedTimer()
    15. Public Event doneTick(ByVal tName As String, ByVal cntTicks As Double)
    16. Public Event reachedTicks(ByVal tName As String, ByVal cntTicks As Double)
    17. Public Sub New(Optional ByVal tName As String = Nothing, Optional ByRef tProgress As ProgressBar = Nothing, Optional ByVal allTime As Double = 0)
    18. _eventTimer.Enabled = False
    19. _eventTimer.Interval = 1000
    20. eventTimerNeeded = allTime * 60
    21. eventTimerName = tName
    22. eventProgressBar = tProgress
    23. resetProgress()
    24. timeMax = GetMins(CInt(allTime))
    25. End Sub
    26. Public Sub eventTimerSwap(Optional ByVal doReset As Boolean = False)
    27. If doReset Then
    28. resetProgress()
    29. eventTimerTicks = 1
    30. End If
    31. _eventTimer.Enabled = Not _eventTimer.Enabled
    32. If _eventTimer.Enabled Then
    33. RaiseEvent startedTimer()
    34. Debug.WriteLine(eventTimerName + ": start" + workingAction)
    35. Else
    36. RaiseEvent stoppedTimer()
    37. Debug.WriteLine(eventTimerName + ": stop" + workingAction)
    38. End If
    39. End Sub
    40. Private Sub eventTimerTick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _eventTimer.Tick
    41. textToProgress(CStr(eventTimerTicks))
    42. If eventTimerTicks = eventTimerNeeded Then
    43. If eventProgressBar.Visible Then eventProgressBar.Value = eventProgressBar.Maximum
    44. eventTimerSwap(True)
    45. RaiseEvent reachedTicks(eventTimerName, eventTimerNeeded)
    46. Else
    47. If eventProgressBar.Visible Then eventProgressBar.Value = CInt(eventTimerTicks)
    48. RaiseEvent doneTick(eventTimerName, eventTimerTicks)
    49. eventTimerTicks += 1
    50. End If
    51. End Sub
    52. Private Sub textToProgress(ByVal writeString As String)
    53. Dim canvas As Graphics = eventProgressBar.CreateGraphics
    54. Dim cFont As New Font("Dina", 8, FontStyle.Regular)
    55. Dim cFontLength As SizeF = canvas.MeasureString(writeString, cFont)
    56. Dim pSize As SizeF
    57. pSize.Width = eventProgressBar.Width
    58. pSize.Height = eventProgressBar.Height
    59. canvas.DrawString(writeString, cFont, New SolidBrush(Color.Black), New PointF(pSize.Width / 2 - (cFontLength.Width / 2.0F), pSize.Height / 2 - (cFontLength.Height / 2.0F)))
    60. canvas.Dispose()
    61. End Sub
    62. Private Sub resetProgress()
    63. eventProgressBar.Maximum = CInt(eventTimerNeeded)
    64. eventProgressBar.Minimum = 0
    65. eventProgressBar.Value = 0
    66. Application.DoEvents()
    67. End If
    68. End Sub
    69. Private Function GetMins(ByVal vSeconds As Integer) As String
    70. Dim min, sec As String
    71. min = CStr(Math.DivRem(vSeconds, 60, vSeconds))
    72. sec = CStr(vSeconds Mod 60)
    73. Return min & ":" & sec
    74. End Function
    75. End Class
    76. Public Class threadingObject
    77. Shared lockerObject As Object = New Object()
    78. Public Event threadDone(ByVal threadName As String)
    79. Public Sub threadingLoop()
    80. threadRunning = True
    81. SyncLock lockerObject
    82. ' action
    83. workingAction = Nothing
    84. End SyncLock
    85. threadRunning = False
    86. RaiseEvent threadDone("threadingLoop")
    87. End Sub
    88. Public Sub reloadLoop()
    89. threadRunning = True
    90. SyncLock lockerObject
    91. ' action
    92. workingAction = Nothing
    93. End SyncLock
    94. threadRunning = False
    95. RaiseEvent threadDone("reloadLoop")
    96. End Sub
    97. End Class


    Beide Timer werden im Hauptthread in den Applicationevents gestartet und in diesem Modul behandelt:

    VB.NET-Quellcode

    1. Module modTimerEvents
    2. Public WithEvents eTimerH As New clsTimer("t1", frmMain.pNext, 0.3)
    3. Public WithEvents eTimerR As New clsTimer("t2", frmMain.pReload, 0.2)
    4. Public WithEvents threadingObject As New threadingObject
    5. Public workingWithMouse As String = Nothing, workingAction As String = Nothing
    6. Private Sub doActions(ByVal tName As String, ByVal cntTicks As Double) Handles eTimerH.reachedTicks
    7. workingAction = "actions"
    8. Dim tActions As New Thread(AddressOf threadingObject.threadingLoop)
    9. tActions.Start()
    10. End Sub
    11. Private Sub doReload(ByVal tName As String, ByVal cntTicks As Double) Handles eTimerR.reachedTicks
    12. workingAction = "reload"
    13. Dim tActions As New Thread(AddressOf threadingObject.reloadLoop)
    14. tActions.Start()
    15. End Sub
    16. Private Sub threadingObject_threadDone(ByVal threadName As String) Handles threadingObject.threadDone
    17. If threadName = "reloadLoop" Then
    18. eTimerR.eventTimerSwap(True)
    19. Else
    20. eTimerH.eventTimerSwap(True)
    21. End If
    22. End Sub
    23. End Module


    Nun ist mein Problem das die Events der Threads ja in sich selber abgefangen werden und nicht im Hauptthread, dadurch werden die Zaehler angehalten und neugestartet im subthread, welche nach dem starten aber wie gewollt fertig ist und somit steht. Ich suche nun einen Weg zu verhindern das eine Aktion durch einen der Timer ausgefuehrt wird solange der andere Timerthread aktiv ist und danach moechte ich den Timer im Hauptthread neustarten (swapEventTimer(true)) und danach _muessen_ aber die aktionen im anderen Thread sofern welche angefallen sind abgearbeitet werden - das klappt ja soweit schon, nur kann ich den timer im Hauptthread nicht neustarten, da ich ja ohne irgendwas nicht weiss ob der Timer nun durch ist _und_ die aktionen fertig sind.

    Es Hilft leider auch nichts die Aktionen direkt in die Events des Timers zu packen - zumindest mir nicht - da ich nicht wuesste wie ich dort sicherstellen kann das die Aktion von timer 2 erst ausgefuehrt wird wenn timer 1 beendet ist oder nicht verloren geht...

    Ich zerbrech mir seit geschlagenen 2 Tagen die Birne und kenn die google Suchergebnise zu Threads auswendig, desweiteren habe ich ein wenig mit delegate gespielt - ohne erfolg, seitdem ich die threads nutze ist mir sogar eher funktion verloren gegangen (der draw auf die progressbar funktioniert nicht mehr).

    Waere super wenn jemand eine Idee haette wie man das loesen kann - am besten bitte mit kurzem Beispiel :/
    sfmStatus @konzeption: 2%
    Hi.

    Ich habe mir den Code eine ganze Weile angeschaut und weiß immer noch nicht was du damit erreichen willst. Anscheinend übergibst du eine ProgressBar an eine Wrapperklasse für einen Timer - aber was passiert dann? Beide Timer ticken vor sich hin - in deren Tick-Eventhandlern werden Threads gestartet, die ein Event auslösen und dann beendet werden. Dadurch wird einer der Timer angehalten und die ProgressBar auf 0 zurückgesetzt. Ich halte das für weniger sinnvoll...

    Ich nehme einfach mal an:
    - Du hast eine ProgressBar
    - Darin wird ein Zeitwert angezeigt
    - Die ProgressBar visualisiert einen fortschreitenden Vorgang (welchen?)

    Du hast dein Problem schon recht genau beschrieben, aber ich denke, dass dein Lösungsansatz unpassend ist. Eine ProgressBar mit 2 Timern und irgendwelchen Thread-Versuchen zu füllen ist einfach nur schlecht. Schreib bitte nochmal genau, was du eigentlich willst (ohne dich auf deinen Code zu beziehen), dann kann ich dir bestimmt helfen.
    Gruß
    hal2000

    mehr Details und Loesungsansatz von mir...

    Hallo,

    ich bin in der Nacht bereits ein Stueck weiter gekommen, aber zuerst erklaere ich mal den Sinn bzw. die Verwendung nochmal im Detail:

    Zum einen dient das Projekt dazu den Umgang mit Maus ein wenig kennen zulernen, ebenso um meine Programmiererfahrungen in .Net zu erweitern (Ich bin ProvideX Programmierer und daher natuerlich nicht nicht 100% fit in .net, eher sagen wir VB6 like 80% - ProvideX aehnelt VB6 sehr stark).
    Auf der anderen Seite habe ich nachdem der Plan feststand diesen immer erweitert und bin nun an einem Punkt bei dem es um 2 Timer geht die ueber 2 Objekte einer Klasse betrieben werden sollen - Formunabhaengig. Diese beiden Timer haben einen Benutzerdefinierten Intervall (z.b. 3 und 5 minuten).
    Beide Timer kriegen wie je eine Referenz von frmMain.p<NAME> - je eine Progressbar (frmMain wird solange am leben erhalten wie mindestens 1 Timer laeuft, laufen keine Timer und frmMain wird beendet ist das Programm zuende).
    Das ganze dient lediglich der Visualisierung der Timer - hat absolut keinen Zusammenhang mit dem Verwendungszweck der Timerevents und klappt auch 100% wie es soll.
    Das Hauptproblem liegt bei der Sache darin, dass die beiden Timer nach Ablauf automatisch stoppen sollen (okay, das is einfach und geht) und dann einen Thread starten der bestimmte Mauspositionenabklappert und Klicks macht (z.B X=100;y=200; X=100; y=200 => linie bilden) - die Mausklasse habe ich bereits fertig und die laeuft ebenfalls so wie sie soll.
    Nun starten also nach 2 und nach 5 min je ein Thread (in der Entwicklungsphase jeweils 0.2 und 0.3 Minuten) dieser muss am Ende allerdings den jeweilgen Timer neustarten - das war bisher mein Hauptproblem und dieses habe ich (sicherlich nicht sehr elegant?) wie folgt erstmal in den Griff bekommen:

    VB.NET-Quellcode

    1. 'clsTimer:
    2. Public Class threadingObject
    3. Shared lockerObject As Object = New Object()
    4. Public Event threadDone(ByVal threadName As Object)
    5. Private s As Threading.SynchronizationContext = Threading.SynchronizationContext.Current
    6. Public Shared eH, eR As clsTimer
    7. Public Sub threadingLoop()
    8. eH.setProgress("Wating...")
    9. SyncLock lockerObject
    10. eH.setProgress("Working...")
    11. Threading.Thread.Sleep(4000)
    12. End SyncLock
    13. s.Post(New Threading.SendOrPostCallback(AddressOf doneThread), eH)
    14. End Sub
    15. Public Sub reloadLoop()
    16. eR.setProgress("Wating...")
    17. SyncLock lockerObject
    18. eR.setProgress("Working...")
    19. Threading.Thread.Sleep(9000)
    20. End SyncLock
    21. s.Post(New Threading.SendOrPostCallback(AddressOf doneThread), eR)
    22. End Sub
    23. ' module:
    24. Private Sub doActions(ByVal tName As String, ByVal cntTicks As Double) Handles eTimerH.reachedTicks
    25. Dim tActions As New Thread(AddressOf threadingObject.threadingLoop)
    26. threadingObject.eH = eTimerH
    27. tActions.IsBackground = True
    28. tActions.Start()
    29. threadCounter += 1
    30. End Sub
    31. Private Sub doReload(ByVal tName As String, ByVal cntTicks As Double) Handles eTimerR.reachedTicks
    32. Dim tActions As New Thread(AddressOf threadingObject.reloadLoop)
    33. threadingObject.eR = eTimerR
    34. tActions.IsBackground = True
    35. tActions.Start()
    36. threadCounter += 1
    37. End Sub
    38. Public Sub doneThread(ByVal threadName As Object) Handles threadingObject.threadDone
    39. Dim t As clsTimer = DirectCast(threadName, clsTimer)
    40. t.setProgress("Done...")
    41. t.eventTimerSwap(True)
    42. threadCounter -= 1
    43. End Sub


    Das funktioniert nun sehr gut, allerdings bin ich dabei gleich auf das naechste Problem gestossen - zum einen mein Wissen, zum anderen das ich zuviele Ideen haben - und zwar moechte ich nun doch lieber das die Timer direkt weiterlaufen (also doReload + doActions starten die Timer direkt wieder und doneThread gibt nur nen erfolg an ne variable weiter und setzt den threadcounter ein runter).
    Im Prinzip funktioniert auch das, allerdings habe ich dann das Problem das die threads im nirvana landen - aber nicht alle, hier der ablauf:

    timerH start (0.3); timerR start (0.2)
    [..tick..tick]
    timerR reachedTicks; doReload -> thread.start() [sleep 10000]; timerR restart
    timerH reachedTicks; doActions -> thread.start() [sleep 15000] landet aber erstmal vorm synclock / ist ja so gewollt; timerH restart
    timerR reachedTicks; doReload -> thread.start() [sleep 10000]; landet aber erstmal vorm synclock / ist ja so gewollt;timerR restart

    Nun kommt der Knackpunkt:

    nun ist der erste timerR fertig und hebt den lock auf, nun beginnt aber der 2te timerR und timerH laeuft ins nirvana (zumindest lt meiner debug.console() - dort krieg ich kein debug vom doActions-thread-synclock

    Ich denke ich sehe da einfach was nicht? Was muss ich denn nu tun? Es ist sicherlich falsch die Threads jeweils in doReload und doActions zu dim'n - bisher kann ich es nicht anders, da ich nicht weiss wie ich sonst sicherstellen kann das der thread jeweils eine neue Instanz von der doReload oder doActions ist...

    Ich hoffe das ganze wurde etwas klarer? Bei bedarf kann ich auch weitergehenden Austausch der Projektdateien per E-Mail und/oder Messanger anbieten...

    Nachtrag
    Ich habe das ganze uebrigens in Threads gelagert da ich nur genau 1 Maus habe und immer nur genau 1 Mauseevent machen kann und wenn beide Timer synchron ihre Aufgabe erledigen das nicht geht. da die Timer 2 verschiedene Programme bedienen werden und wie gesagt nicht zeitgleich aber zeitnah ablaufen koennten. Eine Art Mauseventque per Timer mit der besonderheit das es nicht einfach events sind die ich abarbeiten, sondern nur abarbeite wenn der timer rum ist - im gegensatz zu dauerverarbeitung - ich hoffe das ist klar... Wuesste gerade nicht wie ich das noch anders beschreiben koennte :wacko:
    sfmStatus @konzeption: 2%

    shell schrieb:

    Bei bedarf kann ich auch weitergehenden Austausch der Projektdateien per E-Mail und/oder Messanger anbieten...

    Dein Projekt klingt ganz interessant - und du gibst dir echt Mühe wie kein anderer, dein Problem zu beschreiben - Respekt. Schon alleine deshalb bin ich gewillt, meine Erfahrung mit dir zu teilen... Ich denke, in deinem Code fehlt "nur" ein erweitertes Synchronisierungskonzept für die Threads, die durch die Timer gestartet werden. SyncLock ist ja eher automatisiert (und damit etwas "beschränkt") - es gibt aber noch einige andere Methoden, Threads miteinander kommunizieren zu lassen. Schick mir mal das Projekt per P/N - ich schaue mal drüber und kann vielleicht die Synchronisation einbauen. Wenn ich den kompletten Code einmal debuggen kann, kann ich evtl. hier und da ein paar .NET-spezifische Verbesserungen einbauen. Ich hoffe in P/Ns sind Dateianhänge möglich - habe lange keine mehr verschickt ^^. Ansonsten kannst du das Projekt ja auf einen der vielen OneClick-Hoster hochladen und mir den Link schicken.

    angenehme Nachtruhe XD
    Gruß
    hal2000
    Ich habs dir mal per PN geschicht. Das ich mein Problem beschreibe ist doch selbstverstaendlich - Sonst braeuchte ich mich hier nicht melden, oder?

    Ich bin mir, fuer die Mitlesenden, auch nicht sicher ob es nur an dem Konzept der Synchronisierung liegt, oder daran, dass ich die threads jedesmal komplett neu Dim - Da fehlt mir ein bisschen der VB .net Grundsatz.

    Spoiler anzeigen

    PS: Hab doch glatt was vergessen... Hier die Config die Du zur Laufzeit brauchen wirst:

    Quellcode

    1. [global]
    2. trayActive=True
    3. infoActive=False
    4. infoTopmost=0
    5. autoStart=False
    6. infoMostTop=False
    7. [browser]
    8. reloadActive=True
    9. reloadTime=1
    10. browserName=chrome
    11. [positions]
    12. btnFullScreen_x=-461
    13. btnFullScreen_y=602
    14. btnNormalScreen_x=-354
    15. btnNormalScreen_y=697
    16. btnZoom_x=-356
    17. btnZoom_y=662
    18. btnReload_x=-1306
    19. btnReload_y=48
    20. btnReloadPage_x=-1306
    21. btnReloadPage_y=48
    22. [commerces]
    23. cCount=0
    24. [houses]
    25. hCount=0

    sfmStatus @konzeption: 2%