System.Windows.Forms.Timer和实例变量

时间:2011-06-16 20:14:22

标签: .net event-handling thread-safety instance-variables

我有一个按钮单击事件处理程序,其中包含更新包含表单中的私有非共享实例变量。

我还有一个 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

感谢。

1 个答案:

答案 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