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

时间:2022-02-19 20:34:04

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