作者 主題: 有關跨執行緒作業無效的問題  (閱讀 15225 次)

0 會員 與 1 訪客 正在閱讀本文。

franklai0516

  • 懷疑的國中生
  • **
  • 文章數: 38
    • 檢視個人資料
有關跨執行緒作業無效的問題
« 於: 2008-05-07 11:49 »
我有幾點問題, 想請教諸位前輩

就是當我開啟一個新執行緒,但又企圖存取主執行緒的物件或變數(使用者介面在的執行緒)時,就會發生跨執行緒作業無效這個錯誤。
懇請諸位前輩指點如何解決這個問題, 謝謝 ^^


洋蔥叔叔

  • 區域板主
  • 鑽研的研究生
  • *****
  • 文章數: 830
    • 檢視個人資料
    • 洋蔥叔叔的隨意漫談電腦、網路、.NET、軟體本地化、雜七雜八
回覆: 有關跨執行緒作業無效的問題
« 回覆 #1 於: 2008-05-07 15:27 »
之所以會出現這個錯誤的原因是開發 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
« 上次編輯: 2008-05-07 15:32 由 Demonbane »

franklai0516

  • 懷疑的國中生
  • **
  • 文章數: 38
    • 檢視個人資料
回覆: 有關跨執行緒作業無效的問題
« 回覆 #2 於: 2008-05-07 18:57 »
非常感謝前輩清楚的解說 ^^
我使用前輩所建議的第二方式 ,利用 Delegate (委派)方式解決了問題
謝謝前輩的指導 ^^