具有单次事件处理程序的多个 DispatcherTimer

发布于 2024-12-25 06:45:36 字数 1309 浏览 1 评论 0原文

我正在开发一个项目,需要我对外部设备生成的 5 个独立事件做出反应,然后在一定延迟后执行某些操作。这些事件通常一次发生一个,但有时也可能同时发生。这是一个坏主意吗?如果是的话为什么?

Imports System.Windows.Threading
Class MainWindow

Private TimerList As List(Of DispatcherTimer)

Private Sub Window_Loaded(ByVal sender As System.Object, 
ByVal e As ystem.Windows.RoutedEventArgs) Handles MyBase.Loaded

TimerList = New List(Of DispatcherTimer)

    'Create 5 timers for the 5 events from external device
    For i As Integer = 1 To 5
        TimerList.Add(
            New DispatcherTimer(TimeSpan.FromSeconds(10), DispatcherPriority.Normal,
                            New System.EventHandler(AddressOf tmr_Tick),
                            Me.Dispatcher) With {.Tag = i}
                        )
    Next

End Sub

Public Sub SomeEventFromExternalDevice(ByVal ID As Integer) 'handles...

    Dim CurTimer = TimerList.Find(Function(x) x.Tag = ID)

    If Not CurTimer Is Nothing Then
        CurTimer.Start()
    End If

End Sub

Private Sub tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim ID = DirectCast(sender.tag, Integer)

    Dim curTimer = TimerList.Find(Function(x) x.Tag = ID)

    curTimer.Stop()

    Select Case ID
        'change something on the UI to indicate that event x timer has elapsed
    End Select

End Sub

End Class 

I'm working on a project that will require me to react to 5 separate events generated by an external device and then do something after a certain delay. The events will normally happen one at a time but may occasionally be simultaneous. Is this a bad idea and if so why?

Imports System.Windows.Threading
Class MainWindow

Private TimerList As List(Of DispatcherTimer)

Private Sub Window_Loaded(ByVal sender As System.Object, 
ByVal e As ystem.Windows.RoutedEventArgs) Handles MyBase.Loaded

TimerList = New List(Of DispatcherTimer)

    'Create 5 timers for the 5 events from external device
    For i As Integer = 1 To 5
        TimerList.Add(
            New DispatcherTimer(TimeSpan.FromSeconds(10), DispatcherPriority.Normal,
                            New System.EventHandler(AddressOf tmr_Tick),
                            Me.Dispatcher) With {.Tag = i}
                        )
    Next

End Sub

Public Sub SomeEventFromExternalDevice(ByVal ID As Integer) 'handles...

    Dim CurTimer = TimerList.Find(Function(x) x.Tag = ID)

    If Not CurTimer Is Nothing Then
        CurTimer.Start()
    End If

End Sub

Private Sub tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim ID = DirectCast(sender.tag, Integer)

    Dim curTimer = TimerList.Find(Function(x) x.Tag = ID)

    curTimer.Stop()

    Select Case ID
        'change something on the UI to indicate that event x timer has elapsed
    End Select

End Sub

End Class 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

携余温的黄昏 2025-01-01 06:45:36

也许更好的解决方案是完全摆脱 DispatcherTimer 并使用普通线程来完成此操作。

首先创建一个类来保存单个事件工作包 - 我在此处添加了一种传递延迟值的方法,但如果它不符合您的需求,您可以忽略它。这是一个简单的过程,它使线程休眠一段时间,然后引发一个新事件供您捕获。如果您需要精确度,那么您可以使用秒表或其他东西来实现延迟:

Public Class DelayThread
    ' Task information
    Private _ID As Integer
    Private _delay As Integer
    Event onDelayUp(ByVal ID As Integer)

    ' Constructor
    Public Sub New(ByVal myID As Integer, ByVal myDelay As Integer)
        _ID = myID
        _delay = myDelay
    End Sub

    Public Sub DelayProc()
        System.Threading.Thread.Sleep(_delay)
        RaiseEvent onDelayUp(_ID)
    End Sub
End Class

现在您的设备将触发此处处理的事件:

Public Sub SomeEventFromExtDevice(ByVal ID As Integer) 'Handles ...
        'get a "delay" value from somewhere, if you like
        Dim newDelayTask As New DelayThread(ID, delay)  'added delay here

        AddHandler newDelayTask.onDelayUp, AddressOf DoSomething

        Dim t As New System.Threading.Thread( _
        New System.Threading.ThreadStart(AddressOf newDelayTask.DelayProc))

        t.Priority = Threading.ThreadPriority.Normal 'whatever priority
        t.Start()

End Sub

这样,每次触发事件时,您都会启动一个新线程,该线程等待您的延迟时间,执行 DoSomething,然后终止,并在此过程中自行清理。

这里您将需要一些“DoSomething”过程:

 'declare this somewhere
 Delegate Sub dlgDoSomething(ByVal ID As Integer)

 Public Sub DoSomething(ByVal ID As Integer) 'hooked to onDelayUp @ runtime above
      'using "Me" here - if DoSomething is somewhere else 
      'you may need to refer to the main UI form instead  
      Me.Invoke(New dlgDoSomething(AddressOf uiDoSomething), New Object() {ID})
 End Sub

DoSomething 过程将从每个线程调用,因此它必须在主 UI 线程上调用 - 然后需要:

Public Sub uiDoSomething(ByVal ID As Integer)
        ' Do Something with ID - UI thread is now executing this so you are safe
End Sub

如果知道事件的确切顺序很重要 - 知道它们何时到达和进入什么顺序 - 那么您可以在 SomeEventFromExtDevice 中添加时间戳并将其传递。

您可能还想添加一些用于关闭应用程序的处理 - 例如,这里没有检查来确保线程在处理后不会尝试将其编组到主窗体上。

Probably a better solution to this is to get rid of the DispatcherTimer altogether and do this with plain threads.

First create a class to hold a single event work package - I've added a way to pass the delay value here but you can omit it if it doesn't fit your needs. This is a simple procedure which sleeps the thread for the delay and then raises a new event for you to catch. If you need precision then you can implement the delay with a Stopwatch or something else:

Public Class DelayThread
    ' Task information
    Private _ID As Integer
    Private _delay As Integer
    Event onDelayUp(ByVal ID As Integer)

    ' Constructor
    Public Sub New(ByVal myID As Integer, ByVal myDelay As Integer)
        _ID = myID
        _delay = myDelay
    End Sub

    Public Sub DelayProc()
        System.Threading.Thread.Sleep(_delay)
        RaiseEvent onDelayUp(_ID)
    End Sub
End Class

Now your device will fire events handled here:

Public Sub SomeEventFromExtDevice(ByVal ID As Integer) 'Handles ...
        'get a "delay" value from somewhere, if you like
        Dim newDelayTask As New DelayThread(ID, delay)  'added delay here

        AddHandler newDelayTask.onDelayUp, AddressOf DoSomething

        Dim t As New System.Threading.Thread( _
        New System.Threading.ThreadStart(AddressOf newDelayTask.DelayProc))

        t.Priority = Threading.ThreadPriority.Normal 'whatever priority
        t.Start()

End Sub

This way, each time an event fires you start a new thread which waits for your delay time, does DoSomething and then terminates, cleaning itself up in the process.

Here you will need some "DoSomething" procedure :

 'declare this somewhere
 Delegate Sub dlgDoSomething(ByVal ID As Integer)

 Public Sub DoSomething(ByVal ID As Integer) 'hooked to onDelayUp @ runtime above
      'using "Me" here - if DoSomething is somewhere else 
      'you may need to refer to the main UI form instead  
      Me.Invoke(New dlgDoSomething(AddressOf uiDoSomething), New Object() {ID})
 End Sub

The DoSomething procedure will be called from each thread so it has to invoke on the main UI thread - then need :

Public Sub uiDoSomething(ByVal ID As Integer)
        ' Do Something with ID - UI thread is now executing this so you are safe
End Sub

If knowing the exact order of the events is important - knowing when they arrived and in what order - then you could add a timestamp in the SomeEventFromExtDevice and pass that along also.

You might also want to add some handling for shutting down the application - there are no checks here to make sure that threads are not trying to marshall onto the main form after it has been disposed, for example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文