web-dev-qa-db-ja.com

バックグラウンドワーカーを完全に「殺す」方法は?

私は一連のデジタルIOアクションを繰り返し実行するWindowsアプリケーションを書いています。

この一連のアクションは、ユーザーが[開始]ボタンをクリックすると開始され、backgroundWorker1_DoWork()のバックグラウンドワーカーによって実行されます。

ただし、「このバックグラウンドワーカーは現在ビジー状態です...」というエラーメッセージが表示される場合があります。

Whileループを使用して、別の一連のアクションを開始する前にバックグラウンドワーカーを「強制終了」することにより、コードに次を実装することを考えています。

if (backgroundWorker1.IsBusy == true)
{

    backgroundWorker1.CancelAsync();
    while (backgroundWorker1.IsBusy == true)
    {
        backgroundWorker1.CancelAsync();
    }

    backgroundWorker1.Dispose();

}

backgroundWorker1.RunWorkerAsync();

私の主な関心事は、backgroundWorker1が最終的に「殺される」かどうかだと思います。もしそうなら、完了するのに長い時間がかかりますか?

このコーディングにより、無限ループに陥りますか?

54
Ken Hung

次のようなものを使用できます(マネージスレッドの中止とThreadAbortExceptionの詳細については、「 Rotorを使用したThreadAbortExceptionの深さの配管 クリスセラーズ」を参照してください)。

public class AbortableBackgroundWorker : BackgroundWorker
{

    private Thread workerThread;

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        workerThread = Thread.CurrentThread;
        try
        {
            base.OnDoWork(e);
        }
        catch (ThreadAbortException)
        {
            e.Cancel = true; //We must set Cancel property to true!
            Thread.ResetAbort(); //Prevents ThreadAbortException propagation
        }
    }


    public void Abort()
    {
        if (workerThread != null)
        {
            workerThread.Abort();
            workerThread = null;
        }
    }
}

使用法:

backgroundWorker1 = new AbortableBackgroundWorker();
//...
backgroundWorker1.RunWorkerAsync();

if (backgroundWorker1.IsBusy == true)
{
    backgroundWorker1.Abort();
    backgroundWorker1.Dispose();
}
75

私は、スレッドは、可能な限りリソースをownに責任を持つべきだと考えています独自のライフタイムを含む

通常、スレッドをスコープ外から殺すのは悪い考えです。スレッドにメッセージを渡してシャットダウンするように設計されたアプリケーションitself downは、マルチスレッド動作に関連する問題がはるかに少ない傾向があります。

スレッドは、上記のメッセージを監視する必要があります。これは、別のスレッドによって設定されたブール値と同じくらい簡単で、その監視スレッドによって適切なタイミングで読み取られ、できるだけ早くシャットダウンされますcleanly.

つまり、メッセージを探す必要がある場合:

  • メインループがある場合は、そのループです。
  • 長期実行ループで定期的に。

メッセージでシャットダウンするスレッドは待機する必要があります(もちろん、GUIを停止しないでください)。

外部の強制終了をより安全にするために、スレッドが自分自身をキャンセル可能としてマークできる場合など、特定の機能を持つスレッド環境には他の可能性があることに注意してください。

ただし、通常、アプリケーションを設計して、独自の目的のスレッドマスターを残す方が簡単です。

18
paxdiablo

仕事をする(と思う)ものを1つにまとめます。 im waaaay offがあれば教えてください。これがどのように機能するかの簡単な例です。

var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation = true};

backgroundWorker.DoWork += (sender, args) =>
         {                 
                 var thisWorker = sender as BackgroundWorker;
                 var _child = new Thread(() =>
                                               {
                                                   //..Do Some Code

                                               });
                 _child .Start();
                 while (_child.IsAlive)
                 {
                     if (thisWorker.CancellationPending)
                     {
                         _child.Abort();
                         args.Cancel = true;
                     }
                     Thread.SpinWait(1);
                 }                 
         };

 backgroundWorker.RunWorkerAsync(parameter);
 //..Do Something...
backgroundWorker.CancelAsync();

バックグラウンドワーカーはスレッドプールの一部であるため、中止することは望ましくありません。ただし、内部でスレッドを実行して、アボートの発生を許可できます。 backgroundWorkerは、基本的に、子スレッドが完了するか、プロセスを強制終了するように通知するまで実行されます。その後、バックグラウンドワーカースレッドは読み取りプールに戻ることができます。通常、これをヘルパークラスでラップし、バックグラウンドスレッドをパラメーターとして渡して、子スレッドで実行するデリゲートメソッドを渡します。

誰かが私の頭を壁にぶつけたのかどうか教えてください。でもうまくいくようです。しかし、スレッドの問題はそれではありません。

0
Rob

私は同じ問題を抱えていました、これが役立つかどうかはわかりませんが、あなたのバックグラウンドワーカーがループを持っているか、それが終了することを推測しています。ループ内にループを配置する必要があります。

バックグラウンドワーカーの内部に配置します。

while (backgroundworker1.CancellationPending == false)
{
    //Put your code in here
}

このバックグラウンドワーカーを殺すには、ボタンを入れることができます:

BackgroundWorker1.CancelAsync()

これがお役に立てば幸いです。

0
echrom