System.Windows.Forms.Timer и переменные экземпляра - PullRequest
3 голосов
/ 17 июня 2011

У меня есть обработчик события нажатия кнопки, который, помимо прочего, обновляет личную, не разделяемую переменную экземпляра в содержащей ее форме.

У меня также есть System.Windows.Forms.Timer , чье событие Tick наступает через несколько секунд после завершения события нажатия кнопки.

Мой вопрос: почему обработчик событий Tick иногда (довольно часто) видит предыдущее значение этой переменной экземпляра? (Я думал, что System.Windows.Forms.Timer является поточно-ориентированным по отношению к переменным экземпляра.)

Смежный вопрос: уместно ли, что это часто происходит на очень быстром четырехпроцессорном компьютере, но редко, если вообще когда-либо на медленном двухпроцессорном? Другими словами, возможно ли, что проблема связана с синхронизацией переменных экземпляра между процессорами?

Код следует. Изменены условные обозначения для отображения красоты.

/* 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 Ответ

1 голос
/ 17 июня 2011

Чтобы убедиться, что одновременно выполняется только один блок кода, используйте SyncLock. Это предотвратит изменение значения режима во время тикового события.

Вам нужен уникальный экземпляр ссылочного типа, который будет служить ключом для группы:

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
...