System.InvalidOperationException: 跨執行緒作業無效: 存取控制項 'txtLog' 時所使用的執行緒與建立控制項的執行緒不同。
這個問題很明顯是在多執行緒的物件中,要在另一個執行緒物件裡做動作是會有問題的
我寫了一個小程式重現一下這個問題,
程式很簡單,畫面如下
會出現錯誤的程式碼如下:
Public Class Form3
Dim _testTimer As Timers.Timer
Dim _timerInterval As Integer = 3
Dim _timerCount As Integer
Private Sub btnStopTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStopTimer.Click
''停止時間
Me._testTimer.Stop()
End Sub
Private Sub btnStartTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartTimer.Click
''重新開始時間計數
Me._testTimer.Start()
End Sub
Private Sub txtCmd_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtInterval.TextChanged
''重設時間常數
Me._timerInterval = CInt(txtInterval.Text)
End Sub
Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
txtInterval.Text = Me._timerInterval.ToString
Me._testTimer = New Timers.Timer
Me._testTimer.Interval = Me._timerInterval * 1000
AddHandler _testTimer.Elapsed, AddressOf Me.TimerOnElapsed
Me._testTimer.Start()
txtLog.Text = "Time is Start"
End Sub
#Region "Handle Timer Event"
Private Sub TimerOnElapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
Try
Me._testTimer.Stop()
_timerCount = _timerCount + 1
''錯誤發生在下面那行
txtLog.AppendText(Me._timerCount.ToString & vbCrLf)
Catch ex As Exception
MsgBox(ex.ToString)
Finally
Me._testTimer.Interval = Me._timerInterval * 1000
Me._testTimer.Start()
End Try
End Sub
#End Region
End Class
因為我要 Handle Timer 的 Elapsed 事件,
所以將事件另外 Handle
AddHandler _testTimer.Elapsed, AddressOf Me.TimerOnElapsed
但是要把計數值寫入 TextBox 中會出現"跨執行緒作業無效"的錯誤訊息,
所以多加入委派的 Sub 將寫入 Textbox 的事件重新導向
Public Class Form3
Dim _testTimer As Timers.Timer
Dim _timerInterval As Integer = 3
Dim _timerCount As Integer
''委派加入 TextBox 字串的動作
Private Delegate Sub AppendTextBoxCallback(ByVal txtBox As TextBox, ByVal strValue As String)
Private Sub AppendTextBox(ByVal txtBox As TextBox, ByVal strValue As String)
If Me.InvokeRequired Then
Me.Invoke(New AppendTextBoxCallback(AddressOf Me.AppendTextBox), New Object() {txtBox, strValue})
Else
txtBox.AppendText(vbCrLf & strValue)
End If
End Sub
Private Sub btnStopTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStopTimer.Click
''停止時間
Me._testTimer.Stop()
End Sub
Private Sub btnStartTimer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartTimer.Click
''重新開始時間計數
Me._testTimer.Start()
End Sub
Private Sub txtCmd_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtInterval.TextChanged
''重設時間常數
Me._timerInterval = CInt(txtInterval.Text)
End Sub
Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
txtInterval.Text = Me._timerInterval.ToString
Me._testTimer = New Timers.Timer
Me._testTimer.Interval = Me._timerInterval * 1000
AddHandler _testTimer.Elapsed, AddressOf Me.TimerOnElapsed
Me._testTimer.Start()
txtLog.Text = "Time is Start"
End Sub
#Region "Handle Timer Event"
Private Sub TimerOnElapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
Try
Me._testTimer.Stop()
_timerCount = _timerCount + 1
'txtLog.AppendText(Me._timerCount.ToString & vbCrLf)
'交由委派的 Sub 去工作
AppendTextBox(txtLog, Me._timerCount.ToString)
Catch ex As Exception
MsgBox(ex.ToString)
Finally
Me._testTimer.Interval = Me._timerInterval * 1000
Me._testTimer.Start()
End Try
End Sub
#End Region
End Class
講了那麼多,主要就是要加入 Delegate 這行,
Delegate Sub AppendTextBoxCallback(ByVal txtBox As TextBox, ByVal strValue As String)
並在要做的動作寫在委派的 Sub 中就可以了。
Private Sub AppendTextBox(ByVal txtBox As TextBox, ByVal strValue As String)
If Me.InvokeRequired Then
Me.Invoke(New AppendTextBoxCallback(AddressOf Me.AppendTextBox), New Object() {txtBox, strValue})
Else
''想要做的事情寫在這裡
End If
End Sub

沒有留言:
張貼留言