いくつかの作業をバックグラウンドスレッドに移動する最も簡単な方法は何ですか?
-
26-10-2019 - |
質問
このコードがあるとしましょう。
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
解決
順序を保証する簡単な方法は、同じバックグラウンドスレッドですべてのTimeCosumingTasksを順番に実行することです。
これには、複数のスレッドで複数のTimeCosumingTaskを実行するよりもわずかに時間がかかる場合がありますが、結果を同期し、必要な順序で出力を生成しようとして多くの頭痛を節約できます。
あなたの主な目的がUIを解放してユーザーが何か他の作業を続けることができる場合、1つのスレッドと複数のスレッドを使用する間の完了時間の違いはおそらくごくわずかなので、最も単純なルートに移動します - すべてのすべてのバックグラウンドスレッドを使用してくださいTimeCoSumingTasks。
あなたの主な目的が、タイミコンスタスクの計算をより迅速に完了することである場合、複数のコアを利用して作業の一部を並行して実行する可能性が高いため、複数のスレッドを実行することがおそらくより良いアプローチです。
複数のスレッドの場合に順序を維持するには、結果を自分で注文する必要があります。アキュムレータの温度リストを維持し、各結果をリスト内の適切な場所に挿入することができます(場所 /インデックスが簡単に計算されるか既知の場合)、またはすべてのスレッドがCOALSCEに完了した後に追加の処理ステップを追加できます。複数の結果は、目的の順序で最終出力になります。並列および分散コンピューティングサークルでは、問題を複数のチャンクに分割するプロセスを並行して完了し、結果をコヒーレントな順序に結合するプロセスは「マップ削減」と呼ばれます。
他のヒント
WPF、Winforms、SilverlightなどのGUIアプリケーションでは... バックグラウンドワーカー メインスレッドで発生するさまざまなコールバックをマーシャリングするのに役立つため、UIを更新するときに重要です。 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();
この猫を皮をむく方法はたくさんあります。過去数年間、私は多くを使用しました、 BackgroundWorker
最も一般的で簡単です。しかし、これからは、あなたは考えなければなりません async ctp 行く方法です。少なくとも イントロを確認します 決める前に。シンプルでクリーンで簡潔なコードに関しては、これまでに書かれた中で最もホットなコードの一部です。 :)
タスクを使用します。そうすれば、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)));
});
});
}
UIを更新する前に結果を待つ必要があるため、TimeCosumingTask()のみをバックグラウンドスレッドに移動するという意味はありません。バックグラウンドスレッドからUIを更新する必要があります(ただし、UIスレッドへの呼び出しをマーシャリングします)。
それはあなたの究極の目標に依存しますが、1つの簡単な解決策は、タスクを開始することです。注文が重要な場合、これは機能します。
Task.Factory.StartNew(
() =>
{
foreach(string value in myList)
{
string result = TimeConsumingTask(value);
listBox.Invoke(new Action(() => listBox.Add(result)));
}
});
バックグラウンドワーカーの使用も非常に簡単ですが、より多くのコードがあるため、「単純」ではありません。
- バックグラウンドワーカーを作成します
- Doworkハンドラーを割り当てます
- 進捗ハンドラーを割り当てます
- workerreportsprogressを設定します
- runworkerasync()を呼び出します
注文が問題にならない場合は、を使用できます 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が示唆するように使用する - バックグラウンドプロセスが完了するのを待たずに、ユーザーがUIで作業を続けることを許可することができます。処理が進む前に処理が終了するのを待つ必要がある場合、バックグラウンドで実行することには意味がありません。
と マルチスレッド - 使用 並列拡張ライブラリ, 、おそらく - 複数のスレッドを使用して、繰り返しループを並行して実行して、より速く完了することができます。これは、ユーザーが継続する前にプロセスが終了するのを待っている場合に有益です。
最後に、バックグラウンドで何かを実行している場合は、マルチスレッドを使用してより速く仕上げることができます。