2012-08-12 32 views
14

常見的異常,並在WPF多線程工作時可以得到的是:因爲不同的線程擁有它如何處理跨線程訪問異常?

調用線程不能訪問該對象

什麼是處理這個選項正常嗎?

回答

33

根據不同的情況有不同的選項:

從另一個線程

例如訪問控制用進度信息更新TextBlock。

  • Data Binding

    在這種情況下,你可以做最簡單的事情是避免與控制的直接互動。您只需要bind您想要訪問或修改的對象的類別implementsINotifyPropertyChanged然後設置該對象的屬性。該框架將爲您處理剩下的事情。 (通常情況下,您很少需要直接與UI元素交互,您幾乎總是可以綁定相應的屬性並使用綁定源代替;可能需要直接控制訪問的一種情況是控制創作)。在某些情況下,當試圖修改綁定ObservableCollection<T>,爲此,你需要單獨的數據綁定是不夠的,例如...

  • Dispatching

    你可以派遣您的訪問代碼的線程擁有這個對象,可以通過調用來完成或BeginInvokeDispatcher擁有正在訪問的對象(在另一個線程上獲得此Dispatcher是可能的)。

    例如

    new Thread(ThisThreadStart).Start(); 
    
    void ThisThreadStart() 
    { 
        textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test")); 
    } 
    

    如果不執行的線程的方法上明顯可以使用Dispatcher.CheckAccess要麼派遣或直接執行操作。

    例如

    void Update() 
    { 
        Action action =() => myTextBlock.Text = "Test"; 
        var dispatcher = myTextBlock.Dispatcher; 
        if (dispatcher.CheckAccess()) 
         action(); 
        else 
         dispatcher.Invoke(action); 
    } 
    

    如果一個對象不是DispatcherObject,你仍然需要相關Dispatcher可以使用Dispatcher.CurrentDispatcher在線程創建對象(所以這樣的方法,通過一個線程執行不會對你有任何好)。爲了方便,您通常在應用程序的主UI線程上創建對象;你可以從任何地方使用Application.Current.Dispatcher獲得該線程的Dispatcher

特殊情況:

  • BackgroundWorker

    移動,它會在創建實例(當然這應該是UI線程的線程上ProgressChanged任何控制訪問)

  • 定時器

    在WPF中你可以使用DispatcherTimer爲了方便,它分派給你,讓在Tick任何代碼調用的相關調度。如果您可以將調度委託給數據綁定系統,那麼您當然也可以使用普通定時器。

你可以閱讀更多關於Dispatcher隊列如何工作和WPF一般on MSDN線程。

訪問在另一個線程上

例如創建的對象在後臺加載圖像。

如果有問題的對象不是Freezable你應該在一般只需避免對另一個線程創建它或限制訪問創建線程。如果是Freezable,您只需撥打Freeze即可讓其他線程訪問它。

從另一個線程

即,其實例被更新是用戶代碼的類型訪問的數據對象。如果拋出異常,這種情況可能是由某人使用DependencyObject作爲數據類的基本類型。

這種情況與訪問控制的相同,可以應用相同的方法,但通常應該首先避免它。當然,這允許通過dependency properties簡單的屬性更改通知,這些屬性也可以綁定,但往往不夠,這是不值得放棄線程獨立性。您可以從INotifyPropertyChanged獲得更改通知,並且WPF中的綁定系統本質上是不對稱的,始終存在綁定的屬性(目標)以及此綁定的來源。通常,UI是目標,數據是源,這意味着只有UI組件應該需要依賴項屬性。

+0

那麼這裏是一個愚蠢的問題呢。 Dispatcher.CheckAccess()會引發什麼?「調用線程不能訪問這個對象,因爲不同的線程擁有它」? – 2013-12-05 23:30:43

+1

@RogerWillcocks:從未發生過我。舉一個完整的例子來重現這一點... – 2013-12-06 02:32:52

+0

Application.Current.Dispatcher.Invoke(new Action((=)textBox.Text =「Test」));完美工作!謝謝! – 2017-06-16 15:46:00

0

這將是代碼幾百行,什麼東西我「想通了」。

但摘要:

App_OnStartup 生成後臺線程

回調,

呼叫

Application.Current。MainWindow.Dispatcher.CheckAccess() - 得到異常 Application.Current.Dispatcher.CheckAccess()不是

+1

問題是'Current.MainWindow'步驟,而不是'MainWindow.Dispatcher',所以是的,這不是我所暗示的...... – 2013-12-07 22:04:51

+0

正如我想通的,但給定的MainWindow和Dispatcher不是null,你會期望一種旨在告訴您跨線程訪問是否安全的方法不會引發交叉線程異常 – 2013-12-08 23:37:15

0

我有一個udp監聽器對象,它通過事件進行通信,其中方法/回調是在我的mainWindow中編輯的wpf .cs文件。

事件處理函數被調用的參數,一個是我想在mainWindow.cs

在這個線程由H.B.使用信息顯示在一個列表框消息以上; 我已經加入,測試和使用下面的代碼來處理在WPF中crossthread在我的事件處理程序的回調,但我用一個真實的消息不是一個硬編碼之一:

listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere"))); 

UPDATE:

這是更好,因爲你可以把更多的東西放在匿名函數中。

listBox1.Dispatcher.Invoke((Action)delegate 
{ 
    listBox1.Items.Add(e.ReaderMessage); 
});