3

このコードがあるとしましょう。

foreach(string value in myList)
  {
      string result = TimeConsumingTask(value);
      //update UI
      listBox.Add(result);
  }

string result = TimeConsumingTask(value);バックグラウンドスレッドに移動したい。これをバックグラウンドスレッドに移動する最も簡単で効率的な方法は何ですか?

- Order does matter
- Order doesn't matter
4

7 に答える 7

3

WPF、WinForms、Silverlight などの GUI アプリケーションでは、UI の更新時に重要なメイン スレッドで発生するさまざまなコールバックのマーシャリングを処理するBackgroundWorkerを使用できます。ASP.NET では、非同期ページを見ることができます。

バックグラウンドワーカーで順序が問題になる場合の例を見てみましょう:

var worker = new BackgroundWorker();
worker.DoWork += (obj, args) =>
{
    args.Result = myList.Select(TimeConsumingTask).ToList();
};
worker.RunWorkerCompleted += (obj, args) =>
{
    var result = (IList<string>)args.Result;
    foreach(string value in result)
    {
        listBox.Add(result);
    }
}; 
worker.RunWorkerAsync();
于 2012-01-10T18:17:40.820 に答える
2

順序を保証する簡単な方法は、すべての TimeConsumingTasks を同じバックグラウンド スレッドで順番に実行することです。

これは、複数のスレッドで複数の TimeConsumingTasks を実行するよりも少し時間がかかる場合がありますが、結果を同期して必要な順序で出力を生成しようとする頭痛の種から解放されます。

ユーザーが別の作業を続けられるように UI を解放することが主な目的である場合、1 つのスレッドを使用した場合と複数のスレッドを使用した場合の完了時間の差は、おそらく無視できる程度です。 TimeConsumingTasks。

TimeConsumingTasks の計算をより迅速に完了することが主な目的である場合は、複数のコアを利用して一部の作業を並行して実行する可能性が高いため、複数のスレッドを実行することがおそらくより良いアプローチです。

複数のスレッドの場合に順序を維持するには、結果を自分で順序付けする必要があります。アキュムレータ一時リストを維持し、リスト内の結果の適切な場所に各結果を挿入するか (場所/インデックスが簡単に計算または既知である場合)、またはすべてのスレッドが合体を完了した後に追加の処理ステップを追加することができます。複数の結果が目的の順序で最終出力になります。並列および分散コンピューティング サークルでは、問題を複数のチャンクに分割して並列に完了し、結果を一貫した順序に結合するプロセスを「マップ リダクション」と呼びます。

于 2012-01-10T18:45:50.360 に答える
1

タスクを使用します。こうすることで、UI をブロックすることなく、操作全体がバックグラウンドで実行されます。ただし、必ずメイン スレッドでリスト ボックスを更新してください。WinFormsでそれを行う方法はわかりませんが、WPFでは以下を使用するDispatcher.Invoke()Dispatcher.BeginInvoke()、以下のように実行します。

注文が重要な場合:

Task.Factory.StartNew(
    () =>
        {
            foreach (string value in myList)
            {
                string result = TimeConsumingTask(value);
                //update UI
                Application.Current.Dispatcher.BeginInvoke(
                    (Action)(() => listBox.Add(result)));
            }            
        });

順序が重要でない場合は、 も使用できますParallel.ForEach()

Task.Factory.StartNew(
    () =>
        {
            Parallel.ForEach(myList, 
                value =>
                {
                    string result = TimeConsumingTask(value);
                    //update UI
                    Application.Current.Dispatcher.BeginInvoke(
                       (Action)(() => listBox.Add(result)));
                });            
        });
    }
于 2012-01-10T18:34:16.440 に答える
1

この猫の皮を剥ぐ方法はたくさんあります。過去数年間、私は多くを使用してきましたBackgroundWorkerが、最も一般的で簡単なものです。ただし、今後は、 Async CTPが進むべき道であると考える必要があります。決定する前に、少なくともイントロを確認してください。シンプルさとクリーンで簡潔なコードという点では、これまでに作成されたコードの中で最もホットなコードの 1 つです。:)

于 2012-01-10T18:26:44.920 に答える
1

UI を更新する前に結果を待つ必要があるため、TimeConsumingTask() だけをバックグラウンド スレッドに移動しても意味がありません。バックグラウンド スレッドから UI を更新する必要があります (ただし、UI スレッドへの呼び出しをマーシャリングします)。

最終的な目標によって異なりますが、単純な解決策の 1 つは、タスクを開始することです。これは、順序が重要な場合に機能します。

Task.Factory.StartNew(
    () => 
    {
        foreach(string value in myList)   
        {       
            string result = TimeConsumingTask(value);       
            listBox.Invoke(new Action(() => listBox.Add(result)));   
        } 
    });

BackgroundWorker の使用も非常に簡単ですが、コードが多いため、「単純」ではありません。

  • バックグラウンド ワーカーを作成する
  • DoWork ハンドラを割り当てる
  • Progress ハンドラを割り当てる
  • WorkerReportProgress の設定
  • RunWorkerAsync() を呼び出す
于 2012-01-10T18:23:57.573 に答える
0

マルチスレッドとバックグラウンド処理には大きな違いがありますが、両方を同時に使用できます。

バックグラウンド処理( Darin が提案するように BackgroundWorker を使用) を使用すると、バックグラウンド プロセスが完了するのを待たずに、ユーザーが UI で作業を続行できるようになります。ユーザーが続行する前に処理が完了するのを待たなければならない場合、バックグラウンドで実行しても意味がありません。

マルチスレッド(おそらくParallel Extensions Libraryを使用) を使用すると、複数のスレッドを使用して反復ループを並行して実行できるため、より速く完了できます。これは、ユーザーが処理を続行する前にプロセスが完了するのを待っている場合に役立ちます。

最後に、バックグラウンドで何かを実行している場合は、マルチスレッドを使用することでより速く終了させることができます。

于 2012-01-10T18:29:18.193 に答える
0

順序が重要でない場合は、Parallel.ForEachループを使用できます。( dthorpe と ttymatty のコメントに従って編集)。

using System.Threading.Tasks;

var results = new List<string>();

Parallel.ForEach(myList, value =>
{
    string result = TimeConsumingTask(value);
    results.Add(result);
});

//update UI
listBox.AddRange(results);

プロセス情報を使用して UI をコールバックする場合は、Darin Dimitrov が回答で推奨しているように、 BackgroundWorkerを使用することをお勧めします。

于 2012-01-10T18:21:17.577 に答える