1
  • BackgroundWorker の DoWork 内のループで ViewModel オブジェクトを作成しています
  • 各反復の進行状況を報告し、新しいオブジェクトを引数として渡し、ProgressChanged ハンドラー (UI スレッドと友達) によって取得されます。
  • そのハンドラーで、オブジェクトは、ListBox がバインドされた ObservableCollection に追加されます。

MY ViewModel クラスには 2 つの文字列プロパティ (Filename と ThumbnailPath) が含まれており、その DataTemplate にはこれらのプロパティにバインドされた Label と Image が含まれています。

void bw_DoWork (object sender, DoWorkEventArgs e) {
   List<string> files = e.Argument as List<string>;
   FileInfo fi; int percent;
   for (int i = 0; i < files.Count; i++) {
      FileViewModel newItem = new FileViewModel(files[i]);
      fi = new FileInfo(files[i]);
      percent = i / files.Count * 100;
      bwImportBrowserItems.ReportProgress(percent, newItem);
   }
}

void bw_ProgressChanged (object sender, ProgressChangedEventArgs e) {
    this.observableCollection.Add(e.UserState as FileViewModel);
}

一般的なアイテム数 (30 ~ 50) の場合の一般的な動作: UI が約 2 ~ 3 秒間フリーズします。アイテムの約半分が表示されます。UI が再び短時間フリーズし、残りが追加されます。

これで、ループから UI の更新を呼び出すのは最善のアイデアではないことがわかりました。呼び出しが非常に頻繁に行われ、UI が応答する時間がないため、UI が「グループで」更新され、そのままになっていることがわかりました。その間無反応。

Thread.Sleep(500)ループの最後の行として追加してみました。これは、すべてが正常に機能していることを示すのに役立ちました。これは、この速度低下により、アイテムが無反応になることなく次々とうまく追加されていたためです。

そこで、さまざまな睡眠値を試して、に落ち着きましたThread.Sleep(25)これは理想的ではありませんが、許容範囲内であり、本来あるべき姿にかなり近づいています。

Thread.Sleep がこのような状況での一般的な回避策であるかどうか、また、この状況で人々が使用する一般的な解決策は何かを尋ねたいと思います:まったく無応答にならずにバックグラウンド ループから UI コレクションを更新することです。私もいくつかのアイデアを思いついたので、コメントをいただければ幸いです。

私が考えることができるアイデア:

  1. ReportProgress はあまり頻繁に行わないでください。10 回、または 10 個の新しい項目ごとに制限してください。
  2. ループで実行しないでください。作成する必要がある項目のリストを作成します。DoWork 本体で、リストから 1 つのアイテムをデキューし、インスタンス化して ViewModel インスタンスを返します。RonWorkerCompleted で、UI を更新し、リストが空かどうかを確認し、そうでない場合は再度 WorkerAsync を実行します。
4

1 に答える 1

1

Thread.Sleep がこのような状況での一般的な回避策である場合

最後の手段と考えてください。合計処理時間を増やしています (CPU 負荷ではありません)。

1) ReportProgress をあまり頻繁に行わないでください。10 回、または 10 個の新しい項目ごとに制限してください。

それが基本的な考え方です。新しいアイテムを収集し、ReportProgess を通じてリストをディスパッチします。リストのサイズを調整します。

2) ループで実行しないでください。

可能ですが、Bgw ごとの 1 アイテムはかなり遅く見えます。同じ症状を示すこともあります。

3) ConcurrentQueue を介して分離します。DoWork で Queue を埋め、Dispatcher.Timer で空にすることができます。また、そのタイマーでバッチを処理してみてください。タイマーの優先度とバッチサイズを調整できます。

于 2012-10-20T12:51:59.880 に答える