2009-10-29 28 views
5

WPFで多数のUI要素を逆シリアル化しているときにUIがフリーズしないようにするにはどうすればよいですか?別のスレッドでそれらをロードしようとしているときに、オブジェクトがUIスレッドに属しているというエラーが表示されます。だから、私はUIデータをロードしている間、Vistaの「プログラムが応答しない」というエラーを防ぐためにどのようなオプションが必要ですか?私は単一スレッドのソリューションに頼ることができますか、おそらく複数のUIスレッドに関する何かを見逃していますか?追加のスレッドなしでUIがフリーズするのを防ぐ

+3

Windowsフォームでは、Application.DoEvents()を使用できます。私はそれがWPFで動作するかわからない。 – Qwertie

答えて

11

スレッドを1つだけ使用している場合は、処理量は増えますが、UIはフリーズします。

BackgroundWorkerスレッドを使用すると、発生するタイミングをより詳細に制御できます。&

UIを更新するには、バックグラウンドスレッドのDispatcher.Invokeを使用して、スレッドの境界を越えて呼び出しをマーシャリングする必要があります。

​​
0

あなたはまだ別のスレッドで、あなたの長い処理を行うことができますが、終了したら、あなたはOldNewThingのブログからDispatcher.BeginInvoke(your_UI_action_here)

0

勧告を呼び出すことによって、UIスレッドと同期する必要があります。

スレッドルートを使用して1つのGUIスレッドを作成し、終了したメインGUIスレッドにレポートを戻す別のスレッドにワークロードを送り出すことが最適です。これは、GUIインターフェイスでスレッドの問題に陥ることがないためです。

だから1つのGUIスレッド 作業を行う多くのワーカースレッド。

スレッドのいずれかがハングした場合、ユーザーはアプリケーションを直接制御でき、アプリケーションインターフェイスでの経験をしなくてもスレッドを閉じることができます。これは彼が喜んであなたのユーザーは彼が常に停止ボタンをクリックして停止をクリックして検索以外のコントロールを感じるので、幸せになります。

0

お試しくださいfreezingあなたのUIElements。凍ったオブジェクトは、InvalidOperationExceptionが発生することなくスレッド間で渡すことができるため、UIスレッドで使用する前に、&をバックグラウンドスレッドでフリーズします。

また、個々のデシリアライズをバックグラウンド優先度でUIスレッドに戻すことを検討してください。これは最適ではありません。なぜなら、UIスレッドはこれらのオブジェクトを逆シリアル化する作業をすべて実行しなければならず、個々のタスクとしてそれらをディスパッチすることによっていくつかのオーバーヘッドが追加されますが、少なくともUIをブロックしません優先順位の低いデシリアライゼーション作業に混乱する可能性があります。

+1

これはフリーズ可能ですが、UI要素はフリーズできません。したがって、ジオメトリやブラシなどを作成することについて話しているだけですが、UI要素(ビジュアルクラスや継承チェーン)を構築することについて話しているのであれば、そうではありません。 –

+0

UIElementのポイントがフリーズできません。 –

2

Here is a wonderful blog posting from Dwane Needは、複数のスレッド間でUI要素を操作するためのすべての利用可能なオプションについて説明しています。

あなたは本当に良い処方箋を与えるために十分な詳細を与えていません。たとえば、データバインディングを使用する代わりに、自分でUI要素を作成するのはなぜですか?正当な理由があるかもしれませんが、詳細がなくても良いアドバイスをするのは難しいです。有用と思われる詳細の別の例として、データの各部分に対して深くネストされた複雑なコントロール階層を構築しようとしているのか、単純な形状を描くだけでよいのでしょうか?

2

DispatcherFramesを使用して、コントロールのフローをそのヘッドでオンにすることで、バックグラウンドでUIスレッドでデシリアライズを続行できます。

まず、逆シリアル化中に定期的に制御を取得する方法が必要です。どのデシリアライザを使用していても、オブジェクトのプロパティセットを呼び出す必要があるため、通常はプロパティセッターにコードを追加できます。あるいは、デシリアライザを変更することもできます。いずれにせよ、あなたのコードは、あなたがコントロールを受ける

たびに十分な頻度と呼ばれていることを確認し、すべてを行う必要がある:

  1. DispatcherFrame
  2. キューBeginInvokeメソッドを使用してディスパッチャにイベントを作成することデシリアライザを呼び出すときにセットがディスパッチャ上で実行されているフレームを開始するには、フレーム
  3. 利用PushFrame上= falseを続行

また、自身が必ずヨーヨーを作ります)DoEvents関数でDateTime.Nowをチェック

public partial class MyWindow 
    { 
    SomeDeserializer _deserializer = new SomeDeserializer(); 

    byte[] _sourceData; 
    object _deserializedObject; 

    ... 

    void LoadButton_Click(...) 
    { 
     Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => 
     { 
     _deserializedObject = _deserializer.DeserializeObject(_sourceData); 
     })); 
    } 
    } 

    public class OneOfTheObjectsBeingDeserializedFrequently 
    { 
    ... 

    public string SomePropertyThatIsFrequentlySet 
    { 
     get { ... } 
     set { ...; BackgroundThreadingSolution.DoEvents(); } 
    } 
    } 

    public class BackgroundThreadingSolution 
    { 
    [ThreadLocal] 
    static DateTime _nextDispatchTime; 

    public static void DoEvents() 
    { 
     // Limit dispatcher queue running to once every 200ms 
     var now = DateTime.Now; 
     if(now < _nextDispatchTime) return; 
     _nextDispatchTime = now.AddMilliseconds(200); 

     // Run the dispatcher for everything over background priority 
     var frame = new DispatcherFrame(); 
     Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => 
     { 
     frame.Continue = false; 
     })); 
     Dispatcher.PushFrame(frame); 
    } 
    } 

を(実際にはありません:など

uはDispatcher.BeginInvokeからそれを行う、またはあなたの呼び出し元のコードは、すべてのロックを保持していないことをここではそれがどのように見えるかですこの技法には必要ですが、非直列化中にSomePropertyが頻繁に設定されているとパフォーマンスが向上します。

編集:私はこれを書いた直後に、DoEventsメソッドを実装する簡単な方法があることを認識しました。 DispatcherFrameを使用する代わりに、単に空のアクションでDispatcher.Invokeを使用してください。

public static void DoEvents() 
    { 
     // Limit dispatcher queue running to once every 200ms 
     var now = DateTime.Now; 
     if(now < _nextDispatchTime) return; 
     _nextDispatchTime = now.AddMilliseconds(200); 

     // Run the dispatcher for everything over background priority 
     Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(() => {})); 
    } 
1

私のパネルで項目を移動しているのと同様の問題がありました。優先度がLoadedのDispatcherTimerを使用していたため、UIがフリーズしていました。 DispatcherPriority.Inputに変更するとすぐに問題は解決しました。

関連する問題