BackgroundWorkerとバックグラウンドスレッド


166

Windowsフォームアプリで使用するバックグラウンドスレッドの実装の選択について、文体上の質問があります。現在、私はBackgroundWorker無限(while(true))ループを持つフォームを持っています。このループではWaitHandle.WaitAny、関心のあることが発生するまでスレッドをスヌーズし続けるために使用します。待機しているイベントハンドルの1つは「StopThread」イベントです。これにより、ループを抜けることができます。このイベントは、オーバーライドされたときに通知されForm.Dispose()ます。

BackgroundWorkerファイルをダウンロードしたり、一連のアイテムを処理したりするなど、UIと結び付けたくなくて限りのない操作を意図した場所で読んだことがあります。この場合、「終了」は不明であり、ウィンドウが閉じている場合のみです。したがってBackgroundWorker、この目的ではなくバックグラウンドスレッドを使用する方が適切でしょうか?

回答:


88

あなたの質問に対する私の理解から、あなたはBackgroundWorkeraを標準スレッドとして使用しています。

BackgroundWorkerUIスレッドを拘束したくないものに推奨される理由は、Win Forms開発を行うときにいくつかの素晴らしいイベントを公開するためです。

イベントRunWorkerCompletedは、スレッドが必要な処理を完了したことを通知したり、スレッドProgressChangedのGUIを更新するイベントが進行したりします。

したがって、これらを使用していない場合でも、標準のスレッドを使用して何をする必要があるかはわかりません。


私が確信していないもう1つの問題は、バックグラウンドワーカーが実行されているフォームを破棄しようとしている場合です。私はshutdownevent(ManualResetEvent)に信号を送り、その後しばらくしてDoWorkが正常に終了します。DoWorkが完了するまでに少し時間がかかる場合や、スレッドを作成する方法があるので(それが望ましい)、フォームを先に進めてDisposeを実行する必要があります。バックグラウンドワーカーが実際に終了するまで参加させてから、Disposeフォームの続行?
フレディスミス

BackgroundWorker.IsBusyがあなたが探しているものだと思います。
ParmesanCodice

1
使用のみCancelAsyncのために(とテストCancellationPendingあなたの代わりに、使用した例外持つようにしたい場合は、あなたのスレッドは、短い間隔でポーリングされます場合はSystem.Threading.Thread.Abort()、スレッドブロック自体の中で例外を発生させているが、事態のための右のモデルを選択します。
ブレットライアン

369

私の考えのいくつか...

  1. バックグラウンドで実行され、UIと対話する必要がある単一のタスクがある場合は、BackgroundWorkerを使用します。データのマーシャリングとUIスレッドへのメソッド呼び出しのタスクは、そのイベントベースのモデルを通じて自動的に処理されます。次の場合は、BackgroundWorkerを避けてください。
    • アセンブリはUIを持たないか、UIと直接対話しません。
    • スレッドがフォアグラウンドスレッドである必要がある、または
    • スレッドの優先順位を操作する必要があります。
  2. ThreadPoolを使用する効率が必要な場合はスレッドをます。ThreadPoolは、スレッドの作成、開始、および停止に関連するオーバーヘッドを回避するのに役立ちます。次の場合はThreadPoolの使用を避けます...
    • タスクはアプリケーションの存続期間中実行され、
    • スレッドがフォアグラウンドスレッドである必要があります。
    • スレッドの優先順位を操作する必要がある、または
    • スレッドに固定ID(中止、一時停止、検出)が必要です。
  3. 実行時間の長いタスクや、フォアグラウンドスレッドとバックグラウンドスレッドのどちらかを選択する、スレッドの優先度を調整する、スレッドの実行をきめ細かく制御するなど、正式なスレッドモデルによって提供される機能が必要な場合は、Threadクラスを使用します。

10
バックグラウンドワーカーはSystem.dllアセンブリとSystem.ComponentModel名前空間にあります。Winformsへの依存関係はありません。
Kugel

17
それは正しいですが、BackgroundWorkerスレッドの進行状況を関係者に報告するように設計されています。関係者には、通常UIが含まれます。クラスのMSDNドキュメントは、これを非常に明確にします。タスクをバックグラウンドで実行するだけの場合は、ThreadPoolスレッドの使用をお勧めします。
Matt Davis

5
System.Windows.Forms組み立てについてのあなたのポイントに関して; BackgroundWorkerWPFアプリにも役立ちます。これらのアプリにはWinFormsへの参照がない場合があります。
GiddyUpHorsey '09 / 09/22

12

Matt Davisの発言のほとんどと、次の点が追加されました。

私にとっての主な差別化要因BackgroundWorkerは、を介した完了したイベントの自動マーシャリングですSynchronizationContext。UIコンテキストでは、これはUIスレッドで完了したイベントが発生することを意味するため、UIの更新に使用できます。BackgroundWorkerUIコンテキストでを使用している場合、これは大きな差別化要因です。

を介して実行されたタスクはThreadPool簡単にキャンセルできません(これにはThreadPoolQueueUserWorkItemとデリゲートが非同期で実行されます)。したがって、スレッドのスピンアップのオーバーヘッドを回避しながら、キャンセルが必要な場合は、を使用するBackgroundWorkerか、(おそらくUIの外で)スレッドをスピンアップし、その参照を保持して、を呼び出すことができますAbort()


1
ただ...うまくいけば、アプリケーションはスレッド化されたタスクを停止するクリーンな方法について設計されています(中止は通常そうではありません)

11

また、バックグラウンドワーカーの存続期間中、スレッドプールスレッドを拘束します。スレッドプールスレッドの数は有限であるため、問題になる可能性があります。アプリのスレッドを1度だけ作成する場合(バックグラウンドワーカーの機能を使用しない場合)は、backgroundworker / threadpoolスレッドではなく、スレッドを使用します。


1
これは良い点だと思います。ですから、私がこれから受け取るメッセージは、フォームの存続期間中に「一時的に」バックグラウンドスレッドが必要な場合はバックグラウンドワーカーを使用しますが、フォームの存続期間全体(数分、数時間、日...)その後、BackgroundWorkerの代わりにThreadを使用して、ThreadPoolの目的を誤用しないようにします
freddy smith

「...限られた数しか存在しないので心配になるかもしれません」に関して、OS上の他のアプリがそれらを必要とし、同じ「プール」から共有する可能性があることを意味しますか?
Dan W

8

ご存知のように、Windowsフォーム、WPF、またはその他のテクノロジを使用しているかどうかに関係なく、BackgroundWorkerを使用する方が簡単な場合があります。これらの人たちの良い点は、スレッドがどこで実行されているかについてあまり心配する必要なくスレッドを実行できることです。これは単純なタスクに最適です。

BackgroundWorker最初にスレッドをキャンセルするかどうかを検討する前に(閉じるアプリ、ユーザーのキャンセル)、スレッドがキャンセルを確認するか、それとも実行自体を実行するかを決定する必要があります。

BackgroundWorker.CancelAsync()に設定さCancellationPendingれますtrueが、これ以上何もしません。これを継続的にチェックするのはスレッドの責任です。このアプローチでは、ユーザーがキャンセルしたが、テストの前にスレッドが完了したという競合状態になる可能性があることにも注意してください。CancellationPending

Thread.Abort() 一方、そのスレッドのキャンセルを強制するスレッド実行内で例外をスローしますが、この例外が実行中に突然発生した場合に何が危険であるかについて注意する必要があります。

スレッディングでは、タスクに関係なく、非常に注意深く検討する必要があります。

.NET Frameworkでの並列プログラミング マネージスレッドのベストプラクティス


5

.NETを知る前にスレッドの使い方を知っていたので、BackgroundWorkers を使い始めたとき、慣れるまでに少し時間がかかりました。Matt Davisは、その違いを非常に優れた方法でまとめましたが、コードが何をしているかを正確に理解することはさらに難しく、これによりデバッグが困難になる可能性があります。IMOは、スレッドのプールに作業を割り当てることを考えるよりも、スレッドの作成とシャットダウンを考える方が簡単です。

私はまだ他の人の投稿にコメントすることはできませんので、piers7に対処するための回答を使用する際の一時的な不自由を許してください

Thread.Abort();代わりに使用せず、イベントを通知し、通知されたときにスレッドが正常に終了するようにスレッドを設計します。 スレッドの実行の任意のポイントでThread.Abort()a ThreadAbortExceptionを発生させます。これにより、孤立したモニター、破損した共有状態など、あらゆる種類の不幸なことが起こります。
http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx


2

壊れていない場合は、それが修正されるまで...冗談です:)

しかし、真剣にBackgroundWorkerはおそらくあなたが既に持っているものと非常によく似ています。最初からそれを始めていたなら、おそらく時間を節約できただろう-しかし、現時点では必要性はないと思います。何かがうまくいかなかったり、現在のコードを理解するのが難しいと思わない限り、私はあなたが持っているものを使い続けます。


2

基本的な違いは、あなたが述べたように、からGUIイベントを生成することですBackgroundWorker。スレッドがメインGUIスレッドの表示を更新したりイベントを生成したりする必要がない場合は、単純なスレッドにすることができます。


2

まだ言及されていないBackgroundWorkerクラスの1つの動作を指摘したいと思います。Thread.IsBackgroundプロパティを設定すると、通常のスレッドをバックグラウンドで実行できます。

バックグラウンドスレッドは、プロセスの終了を妨げないことを除いて、フォアグラウンドスレッドと同じです。[ 1 ]

この動作をテストするには、フォームウィンドウのコンストラクターで次のメソッドを呼び出します。

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

IsBackgroundプロパティがtrueに設定されているときにウィンドウを閉じると、アプリケーションは正常に終了します。

ただし、IsBackgroundプロパティがfalse(デフォルト)に設定されているときにウィンドウを閉じると、ウィンドウだけが表示されなくなりますが、プロセスは引き続き実行されます。

BackgroundWorkerクラスは、バックグラウンドで実行されるスレッドを利用します。


1

バックグラウンドワーカーは、別のスレッドで機能するクラスですが、単純なスレッドでは得られない追加の機能(タスクの進行状況レポートの処理など)を提供します。

バックグラウンドワーカーによって提供される追加機能が必要ない場合-必要ないと思われる場合-スレッドの方が適切です。


-1

私にとって困惑しているのは、ビジュアルスタジオデザイナーは、実際にはサービスプロジェクトで機能しないBackgroundWorkersとTimersしか使用できないことです。

それはあなたのサービスにきちんとしたドラッグ・アンド・ドロップ・コントロールを与えます、しかしそれを配備しようとさえしないでください。うまくいきません。

サービス:System.Timers.Timerのみを使用してください。System.Windows.Forms.Timerは、ツールボックスで使用できても機能しません。

サービス:サービスとして実行されている場合、BackgroundWorkersは機能しません。代わりにSystem.Threading.ThreadPoolsを使用するか、非同期呼び出しを使用してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.