我有一个按钮单击事件处理程序,其中包含更新包含表单中的私有非共享实例变量。
我还有一个 System.Windows.Forms.Timer ,其 Tick 事件在该按钮点击事件完成后几秒钟出现。
我的问题:为什么 Tick 事件处理程序有时(经常)会看到该实例变量的先前值? (我认为System.Windows.Forms.Timer对于实例变量是线程安全的。)
相关问题:在非常快的四处理器计算机上经常发生这种情况是否相关,但在慢速双处理器上却很少发生?换句话说,该问题是否可能与跨CPU同步实例变量有关?
代码如下。为展示美容修改了评论惯例。
/* Instance variable get/set */
Public Property mode() As modetype
Get
Return _mode
End Get
Set(ByVal value As modetype)
_mode = value
Select Case value
/* Lots of mode-specific processing here */
End Select
Debug.Assert(mode = value)
End Set
End Property
/* Click event handler */
Private Sub btnClear_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnClear.Click
Debug.Assert(Not (picVideo Is Nothing))
mode = modetype.clear
End Sub
/* Tick event handler */
Private Sub tmrCapture_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrLiveImageCapture.Tick
// FOLLOWING LINE is where mode should be .clear but comes up as .live instead.
If mode = modetype.live Then
Debug.Assert(mode = modetype.live) // Seriously? Yes.
Try
execute_timer_tick_stuff()
Catch ex As Exception
/* Shouldn't happen */
tmrLiveImageCapture.Stop() // FIXME: can you stop a timer within its own tick event?
MessageBox.Show("Error in live timer tick: " & ex.Message)
Debug.Assert(Not tmrLiveImageCapture.Enabled)
End Try
End If
End Sub
感谢。
答案 0 :(得分:1)
为了确保只有一个代码块同时执行,请使用SyncLock。这将阻止模式的值在tick事件期间追赶。
您需要一个唯一的引用类型实例作为该组的键:
Dim TestSyncLock As New Object()
现在一次只能执行以下一个块;其他人等到整个SyncLock块完成,然后另一个SyncLock才有机会执行。
SyncLock TestSyncLock
DoSomethingTricky()
End SyncLock
SyncLock TestSyncLock
DoSomethingElseTricky()
End SyncLock
一次执行整个代码块而不中断称为原子操作。试试这个代码:
Private modeSyncLock As New Object()
/* Instance variable get/set */
Public Property mode() As modetype
Get
Return _mode
End Get
Set(ByVal value As modetype)
/* If we have entered the tick handler's synclock, wait until it's done */
SyncLock modeSyncLock
_mode = value
End SyncLock
Select Case value
/* Lots of mode-specific processing here */
End Select
Debug.Assert(mode = value)
End Set
End Property
/* Click event handler */
Private Sub btnClear_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnClear.Click
Debug.Assert(Not (picVideo Is Nothing))
mode = modetype.clear
End Sub
/* Tick event handler */
Private Sub tmrCapture_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmrLiveImageCapture.Tick
/* If we have entered the mode set, wait until it's done before proceeding */
SyncLock modeSyncLock
If mode = modetype.live Then
Debug.Assert(mode = modetype.live) // Seriously? Yes.
Try
execute_timer_tick_stuff()
Catch ex As Exception
/* Shouldn't happen */
tmrLiveImageCapture.Stop() // FIXME: can you stop a timer within its own tick event?
MessageBox.Show("Error in live timer tick: " & ex.Message)
Debug.Assert(Not tmrLiveImageCapture.Enabled)
End Try
End If
End SyncLock
End Sub