System.Windows.Forms.Timer and instance variables

I have a button click event handler that, among other things, updates a private instance variable that is not shared in the contained form.

I also have System.Windows.Forms.Timer , whose Tick event occurs a few seconds after the completion of this button click event.

My question is: why does the Tick event handler sometimes (quite often) see the previous value of this instance variable? (I thought System.Windows.Forms.Timer is thread safe with respect to instance variables.)

Related question: Is it appropriate that this happens often on a very fast four-processor computer, but rarely, if ever, on a slow two-processor computer? In other words, is it possible that the problem has something to do with instance variable synchronization between processors?

The 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

Thank.

+3
source share
1 answer

To ensure that only one block of code is running at a time, use SyncLock. This will prevent the mode value from being exceeded during a tick event.

You need a unique instance of the reference type that will serve as the key for the group:

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 synclock, wait until it 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 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

All Articles