之所以會出現這個錯誤的原因是開發 Windows  介面應用程式有個鐵則就是不應該跨執行緒去存取介面。如果這樣做的話很容易造成介面上資料的不一致/損毀,或者是其他不可預期的事情發生。
你所謂主執行緒的物件或變數應該是指表單介面上的物件吧。如果不是的話,你應該還是可以存取不會有錯誤,只不過資料的完整性要靠自己用程式碼控制。如果是的話,有幾個方式,第一個比較不安全,不建議使用,但很簡單(我範例都用 VB.NET 程式碼):
在表單的 Load 事件裡面加上

Form.CheckForIllegalCrossThreadCalls = False

他就會讓你跨執行緒處理,錯誤訊息不會出現,不過等於是違反了上述的原則,後果自負。

第二個方式是利用 Delegate (委派) 將存取 UI 的呼叫 marshal 到主執行緒,算是正統的做法,安全,但比較麻煩:先建立一個拿來更新 UI 的方法,如同下面的 UpdateUI,然後再建立一個有相同 signature 的委派,下面叫 UpdateUICallBack

    Private Delegate Sub UpdateUICallBack(ByVal newText As String, ByVal c As Control)

    Private Sub UpdateUI(ByVal newText As String, ByVal c As Control)
        If Me.InvokeRequired() Then
            Dim cb As New UpdateUICallBack(AddressOf UpdateUI)
            Me.Invoke(cb, newText, c)
        Else
            c.Text = newText
        End If
    End Sub

之後需要改任何控制項的文字 (i.e Text 屬性) 就直接叫用 UpdateUI 就好了,跨執行緒存取也不會有問題,e.g:

   UpdateUI("Updated from another thread", TextBox1)

第三個方式是使用 BackgroundWorker 元件,這是微軟因應多執行緒的需求提供的,也不錯用,不過他背後跟上面一樣也還是 marshal 到主執行緒:
把一個 BackgroundWorker 元件拉到你的表單上,把 WorkerReportsProgress 跟 WorkerSupportsCancellation 屬性都設成 True
再來建立這三個事件的 Event Handler

DoWork - 這是主要執行工作的地方,會在主執行緒以外的執行緒執行,不要在這裡更新 UI,需要的時候叫 BackgroundWorker 元件的 ReportProgress 就好
ProgressChanged - 當 ReportProgress  被呼叫的時候這個事件就會發生,這裡是由主執行緒在執行,可以任意存取 UI
RunWorkerCompleted - DoWord 結束後這個事件就會發生,也是由主執行緒在執行

最後只要叫 BackgroundWorker 的 RunWorkerAsync() 方法就會開始執行

有關 BackgroundWorker 的詳細資訊請看:
http://msdn.microsoft.com/zh-tw/library/system.componentmodel.backgroundworker.aspx




from:http://phorum.study-area.org/index.php?topic=51654.0

 

 

 

 

------------------sample code-------------

UpdateUI("StartTestFlow", textBox)
SetControlEnabled(commandBotton, False)

    Private Delegate Sub UpdateUICallBack(ByVal newText As String, ByVal c As Control)
    Private Delegate Sub SetControlEnabledCallBack(ByVal c As Control, ByVal value As Boolean)
    Private Sub UpdateUI(ByVal newText As String, ByVal c As Control)
        If Me.InvokeRequired() Then
            Dim cb As New UpdateUICallBack(AddressOf UpdateUI)
            Me.Invoke(cb, newText, c)
        Else
            c.Text = newText
        End If
    End Sub
    Private Sub EnableUI(ByVal c As Control)
        c.Enabled = True
    End Sub
    Private Sub SetControlEnabled(ByVal c As Control, ByVal value As Boolean)
        If (Me.InvokeRequired) Then
            Dim cb As New SetControlEnabledCallBack(AddressOf SetControlEnabled)
            Me.Invoke(cb, New Object() {c, value})
        Else
            c.Enabled = value
        End If
    End Sub

arrow
arrow
    全站熱搜

    天才R 發表在 痞客邦 留言(1) 人氣()