2

C++ を使用して Windows フォーム GUI アプリケーションでマルチスレッドを使用する必要性に遭遇しました。このトピックに関する私の調査から、バックグラウンドワーカースレッドが私の目的に適しているようです。私が持っているコード例によると

System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) 
{
    BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    e->Result = SomeCPUHungryFunction( safe_cast<Int32>(e->Argument), worker, e );
}

しかし、私がまっすぐに理解する必要があることがいくつかあります

  • バックグラウンド ワーカー スレッドを使用すると、マルチスレッド処理が容易になりますか?
  • なぜ e->Result が必要なのですか?
  • backgroundWorker1_DoWork 関数に渡される引数は何ですか?
  • パラメータ safe_cast(e->Argument) の目的は何ですか?
  • CPUHungryFunction() で何をすればよいですか?
  • CPUHungryFunction() に無期限にループする while ループがある場合はどうなりますか?
  • ワーカー スレッドが取得するプロセッサ時間を制御できますか?
  • 設定された期間内にループがループする回数をより具体的に制御できますか? 1秒間に30回ループするだけでいいのに、1秒間に1000回ループしてCPUを使い果たしたくありません。※GUIの更新レートを制御する必要はありますか?
4

2 に答える 2

3

バックグラウンドワーカースレッドを使用すると、マルチスレッドの作業が楽になりますか?

はい、とてもそうです。これは、ワーカースレッドからUIを更新できないという事実に対処するのに役立ちます。特に、ProgressChangedイベントを使用すると進行状況を表示でき、RunWorkerCompletedイベントを使用すると、クロススレッドの問題に対処することなく、ワーカースレッドの結果を使用してUIを更新できます。

なぜe->Resultが必要なのですか?

行った作業の結果をUIスレッドに戻すため。RunWorkerCompletedイベントハンドラーのe->Resultプロパティで値を取得します。次に、その結​​果でUIを更新します。

関数に渡される引数は何ですか?

ワーカースレッドに何をすべきかを指示するために、それはオプションです。それ以外の点では、引数を任意のメソッドに渡すのと同じですが、引数を選択できないため、さらに厄介です。通常、UIからある種の値を渡します。たとえば、複数の値を渡す必要がある場合は、小さなヘルパークラスを使用します。ワーカーでUI値を取得しようとするよりも、常にこれを優先します。これは非常に面倒です。

CPUHungryFunction()で何をすべきですか?

もちろんCPUサイクルを焼きます。または、一般に、dbaseクエリのように、時間がかかることを行います。これはCPUサイクルを消費しませんが、結果を待っている間にUIスレッドが停止するのに時間がかかりすぎます。大まかに言って、1秒以上かかることを行う必要がある場合は、UIスレッドではなくワーカースレッドで実行する必要があります。

CPUHungryFunction()に無期限にループするwhileループがある場合はどうなりますか?

そうすれば、あなたの労働者は決して完了せず、結果を生み出すこともありません。これは便利かもしれませんが、一般的ではありません。通常、これにはBGWを使用せず、IsBackgroundプロパティがtrueに設定されている通常のスレッドのみを使用します。

ワーカースレッドが取得するプロセッサ時間を制御できますか?

Thread.Sleep()を呼び出して、人為的に速度を落とすことで、いくつかの問題が発生します。これは一般的なことではありません。ワーカースレッドを開始するポイントは、作業を行うことです。スリープ状態のスレッドは、非生産的な方法で高価なリソースを使用しています。

設定された期間内にループがループする回数をより具体的に制御できますか?1秒間に30回ループするだけでよいのに、1秒間に1000回ループするCPUを使い果たしたくありません。

上記と同じように、あなたは眠らなければならないでしょう。これを行うには、ループを30回実行してから、1秒間スリープします。

GUIの更新速度を制御する必要がありますか?

はい、それは非常に重要です。ReportProgress()は、1秒間に何千ものUI更新を生成する消防ホースになる可能性があります。UIスレッドがそのレートに追いつかない場合、これで簡単に問題が発生する可能性があります。UIスレッドは、UIのペイントや入力への応答など、通常の業務を停止します。ProgressChangedイベントハンドラーを実行するために別のinvoke要求を処理し続ける必要があるためです。副作用は、UIがフリーズしているように見えることです。これにより、ワーカーで解決しようとしていた正確な問題が発生します。実際にはフリーズしていません。そのように見えますが、まだイベントハンドラーを実行しています。ただし、ユーザーには違いはわかりません。

覚えておくべきことの1つは、ReportProgress()は人間の目を幸せに保つだけでよいということです。1秒間に20回を超える頻度で発生する更新を確認することはできません。それを超えると、それはただ読めないぼやけに変わります。したがって、とにかく役に立たないUIの更新に時間を無駄にしないでください。また、消火ホースの問題も自動的に回避されます。更新レートの調整はプログラムする必要があり、BGWには組み込まれていません。

于 2012-12-22T12:59:20.333 に答える
1

質問ごとにお答えします

  1. はい
  2. DoWork は void メソッドです (そうする必要があります)。またDoWork、呼び出しスレッドとは別のスレッドで実行されるため、呼び出しスレッドに何かを返す方法が必要です。e->Result パラメータはRunWorkerCompletedRunWorkerCompletedEventArgs
  3. sender 引数は、UI スレッドのイベントを発生させるために使用できる backgroundworker 自体です。DoWorkEventArgs最終的には、呼び出しスレッド (を呼び出したスレッド) から渡されたパラメーターが含まれますRunWorkerAsync(Object)
  4. あなたがする必要があるものは何でも。DoWork スレッドからアクセスできないユーザー インターフェイス要素に注意を払う。通常、完了した作業のパーセンテージを計算し、UI (プログレス バーなど) を更新し、ReportProgress を呼び出して UI スレッドと通信します。WorkerReportProgress(プロパティを True に設定する必要があります)
  5. 無期限に実行されるものはありません。いつでもコードを抜くことができます。真剣に、それは単なる別のスレッドであり、OS がそれを処理し、アプリが終了するとすべてを破棄します。
  6. これで何を意味するのかわかりませんが、おそらく次の質問に関連しています
  7. Thread.Sleep または Thread.Join メソッドを使用して、1 回のループ後に CPU 時間を解放できます。スリープする正確なタイミングは、現在のシステムのワークロード、およびプロセッサの生の速度に応じて微調整する必要があります。

BackgroundWorkerおよびThreadクラスについては、MSDN のドキュメントを参照してください。

于 2012-12-22T09:36:12.020 に答える