I have a button-click event handler that among other things updates a private, non-shared instance variable in the containing Form.
我有一个按钮单击事件处理程序,其中包含更新包含表单中的私有非共享实例变量。
I also have a System.Windows.Forms.Timer, whose Tick event comes around a few seconds after that button click event has completed.
我还有一个System.Windows.Forms.Timer,其Tick事件在该按钮单击事件完成后几秒钟到来。
My question: Why does the Tick event handler sometimes (quite often) see the previous value of that instance variable? (I thought that System.Windows.Forms.Timer is thread-safe with regard to instance variables.)
我的问题:为什么Tick事件处理程序有时(经常)看到该实例变量的先前值? (我认为System.Windows.Forms.Timer对于实例变量是线程安全的。)
Related question: Is it relevant that this happens frequently on a very fast quad-processor computer but rarely if ever on a slow dual-processor? In other words, is it possible that the issue has something to do with synchronizing instance variables across CPUs?
相关问题:在非常快的四处理器计算机上经常发生这种情况是否相关,但在慢速双处理器上很少发生?换句话说,问题是否可能与跨CPU同步实例变量有关?
Code follows. Comment conventions modified for display beauty.
代码如下。为展示美感而修改的评论惯例。
/* 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
Thanks.
1 个解决方案
#1
1
In order to make sure only one block of code executes at the same time, use SyncLock. This will prevent the value of mode from catching up during the tick event.
为了确保只有一个代码块同时执行,请使用SyncLock。这将阻止模式的值在tick事件期间赶上。
You need a unique reference type instance to serve as a key to the group:
您需要一个唯一的引用类型实例作为该组的键:
Dim TestSyncLock As New Object()
Now only one of the following blocks can execute at a time; the others wait until an entire SyncLock block is completed before another SyncLock gets a chance to execute.
现在,一次只能执行以下块中的一个;其他人等待,直到整个SyncLock块完成,然后另一个SyncLock才有机会执行。
SyncLock TestSyncLock
DoSomethingTricky()
End SyncLock
SyncLock TestSyncLock
DoSomethingElseTricky()
End SyncLock
Executing an entire block of code at once without interruption is called an atomic operation. Try this for your code:
在不中断的情况下一次执行整个代码块称为原子操作。试试这个代码:
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
#1
1
In order to make sure only one block of code executes at the same time, use SyncLock. This will prevent the value of mode from catching up during the tick event.
为了确保只有一个代码块同时执行,请使用SyncLock。这将阻止模式的值在tick事件期间赶上。
You need a unique reference type instance to serve as a key to the group:
您需要一个唯一的引用类型实例作为该组的键:
Dim TestSyncLock As New Object()
Now only one of the following blocks can execute at a time; the others wait until an entire SyncLock block is completed before another SyncLock gets a chance to execute.
现在,一次只能执行以下块中的一个;其他人等待,直到整个SyncLock块完成,然后另一个SyncLock才有机会执行。
SyncLock TestSyncLock
DoSomethingTricky()
End SyncLock
SyncLock TestSyncLock
DoSomethingElseTricky()
End SyncLock
Executing an entire block of code at once without interruption is called an atomic operation. Try this for your code:
在不中断的情况下一次执行整个代码块称为原子操作。试试这个代码:
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