19

WPFで複数のスレッドを操作するときに発生する可能性のある一般的な例外は、次のとおりです。

別のスレッドがオブジェクトを所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません

これに適切に対処するためのオプションは何ですか?

4

3 に答える 3

46

状況に応じて、さまざまなオプションがあります。

別のスレッドからコントロールにアクセスする

たとえば、進行状況情報でTextBlockを更新します。

  • データバインディング

    この場合、あなたができる最も簡単なことは、コントロールとの直接の相互作用を避けることです。アクセスまたは変更するプロパティを、クラスが実装するオブジェクトにバインドしてから、代わりにそのオブジェクトにプロパティを設定することができます。フレームワークが残りを処理します。(一般に、UI要素を直接操作する必要はほとんどありません。ほとんどの場合、それぞれのプロパティをバインドして、代わりにバインドソースを操作できます。直接制御アクセスが必要になる場合の1つは、制御オーサリングです。) INotifyPropertyChanged

    データバインディングだけでは不十分な場合があります。たとえば、バインドを変更しようとする場合、ObservableCollection<T>このために必要なのは...

  • 派遣

    アクセスコードをオブジェクトを所有するスレッドにディスパッチできます。これは、アクセスするオブジェクトを呼び出すかInvoke所有することで実行できます(これは別のスレッドで可能です)。BeginInvokeDispatcherDispatcher

    例えば

    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関連付けが必要な場合(したがって、スレッドによって実行されているメソッドでこれを行うと、何の役にも立ちません)。通常、アプリケーションのメインUIスレッドでオブジェクトを作成するので便利です。を使用して、どこからでもそのスレッドを取得できます。DispatcherDispatcher.CurrentDispatcher DispatcherApplication.Current.Dispatcher

特殊なケース:

  • BackgroundWorker

    ProgressChangedインスタンスを作成したスレッド(もちろんUIスレッドである必要があります)で発生するコントロールアクセスをに移動します

  • タイマー

    WPFではDispatcherTimer、便利なためにを使用できます。ディスパッチャが自動的に実行されるため、のコードTickは関連付けられたディスパッチャで呼び出されます。ディスパッチをデータバインディングシステムに委任できる場合は、もちろん通常のタイマーも使用できます。

Dispatcherキューの動作とWPFスレッドの一般的な詳細については、MSDNを参照してください。

別のスレッドで作成されたオブジェクトへのアクセス

たとえば、背景に画像をロードします。

問題のオブジェクトがそうでないFreezable場合は、一般に、別のスレッドでオブジェクトを作成したり、作成中のスレッドへのアクセスを制限したりすることは避けてください。その場合は、他のスレッドからアクセスできるようにするためFreezableに呼び出す必要があります。Freeze

別のスレッドからデータオブジェクトにアクセスする

つまり、インスタンスが更新されるタイプはユーザーコードです。例外がスローされた場合、この状況はおそらくDependencyObjectデータクラスの基本型として使用している誰かによって発生しました。

この状況は、コントロールへのアクセスと同じであり、同じアプローチを適用できますが、通常、そもそも回避する必要があります。確かに、これにより、依存関係プロパティを介した単純なプロパティ変更通知が可能になり、それらのプロパティをバインドすることもできますが、多くの場合、これはスレッドの独立性を放棄する価値がありません。から変更通知を受け取ることができINotifyPropertyChanged、WPFのバインディングシステムは本質的に非対称であり、バインドされた(ターゲット)プロパティと、このバインディングのソースである何かが常に存在します。通常、UIがターゲットであり、データがソースです。つまり、UIコンポーネントのみが依存関係プロパティを必要とします。

于 2012-08-12T16:36:58.493 に答える
0

私が「考え出した」ものの場合、それは数百行のコードになります。

しかし、要約は次のとおりです。

App_OnStartupはバックグラウンドスレッドを生成します

コールバックでは、

電話

Application.Current.MainWindow.Dispatcher.CheckAccess()-例外を取得しますApplication.Current.Dispatcher.CheckAccess()は取得しません

于 2013-12-07T05:30:25.280 に答える
0

mainWindowwpf.csファイルでメソッド/コールバックが+=されているイベントを介して通信するudpリスナーオブジェクトがあります。

イベントハンドラー関数はパラメーターを使用して呼び出されます。1つは、mainWindow.csのリストボックスに表示したいメッセージです。

上記のHBによるこのスレッドの情報の使用; 次のコードを使用して、eventhandlerコールバックのwpfでクロススレッドを追加、テスト、および処理しましたが、ハードコードされたメッセージではなく実際のメッセージを使用します。

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

アップデート:

これは、匿名関数により多くのものを入れることができるため、より優れています。

 listBox1.Dispatcher.Invoke((Action)delegate 
 {
     listBox1.Items.Add(e.ReaderMessage); 
 });
于 2017-03-22T17:13:36.107 に答える