2017-07-06 7 views
0

次のコードは簡単な概念証明です。 Windowsフォームアプリケーションには、クリーンアップを実行するFormClosingイベントハンドラがあります。アプリケーションは、Control.Invokeを使用してフォームに書き込むいくつかのスレッドを起動します。私はControl.BeginInvokeを使用することができることを知っていますが、何が起こるかをよりよく理解し、別の解決策を見つけたいと思います。Control.Invokeを使用した予期しないブロック条件

List<Thread> activeThreadlist; 
volatile bool goOnPolling = true; 
private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     goOnPolling = false; 
     Thread.Sleep(1000); 
     foreach (Thread element in activeThreadlist) 
      if (element.IsAlive) 
       element.Abort(); 

    } 

フラグがスレッドにループを停止するためにはfalseに設定されている、彼らが終了する時間を持って、1つはまだ生きているならば、それはアボートで終了します。

ここ

が他のスレッドによって実行されるコードである:彼らは睡眠時に終了しないようにフォームがControl.Invoke上のスレッドブロックを閉じているとき症例の約50%において

while (goOnPolling) 
{ 
    //some long elaboration here 
    if (!goOnPolling) continue; 
    aControl.Invoke(new Action(() => 
    { 
     //display some result about the elaboration 
    })); 
    if (!goOnPolling) continue; 
    //some long elaboration here  

} 

Abortが呼び出されます。私は、呼び出しの前にgoOnPollingをチェックして、99.999%のケースで十分だったと思ったが、間違っていた。コードに記載されているように、スレッドは長いエラボレーション(少なくとも100ms)を行いますので、私は期待していますgoOnPollingはそれらの間に変更される可能性があります。なぜ私は間違っていますか?追加のスレッドを作成するBeginInvokeに繰り返しない別の簡単なソリューションがありますか?

更新

私はタイトルが間違っていた実現のコメントを読んだ後。私は最初にデッドロックを書きましたが、予期せぬ(私にとっては)ブロック状態に過ぎません。最初は1000ミリ秒間待ってからスレッドがまだ生きていた理由を理解できませんでしたので、アボートを見つけてください。 InvokeBeginInvokeの違いを完全に誤解しています。

@ジョンB

私は休憩を続けるが、そのコードは内部しばらく(goOnPolling)ブロックであるので、私は、より多くの何も唯一のいくつかのCPUサイクルを節約することができなかったよりも適しています同意するものとします。

理由がよくわからない理由goOnPollingはInvokeの初期段階で変更されますか?私が長い精緻化を実行する場合、それはほとんどの時間に起こるはずです。

+1

'BeginInvoke'は追加のスレッドを作成するべきではなく、既存の* UIスレッドに作業をキューイングするだけです。新しいスレッドを作成することは、 'BeginInvoke'が行うように設計されていることに完全に反するでしょう。 –

+1

最後の文章は本当ですか?私が知る限り、 'Invoke'と' BeginInvoke'の両方がUIスレッド上で実行されます。戻り前に 'Invoke'ブロックのみが実行されます。即ち、「Invoke」=「BeginInvoke」+実行を待つ。あなたは 'delegate'' Invoke'/'BeginInvoke'と混同されるかもしれません、https://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvokeを見てください。 'Control.Invoke'を使用する唯一の理由は、呼び出されたアクションを完了して呼び出しスレッドを続行する必要がある場合です。 – Rotem

+1

私は100%陽性です。 'Invoke'はメッセージループの実行をキューに入れ、完了するまで待機します。 'BeginInvoke'は全く同じことをしますが、完了するまでブロックしません。おそらくあなたのデッドロックがどこから来ているのでしょうか。あなたのUIスレッドは子スレッドで待機しており、子スレッドはUIを完了するのを待っています。 –

答えて

0

これらの他のスレッドはInvokeを通してUIスレッドにアクセスしたいと思っており、スレッドをSleepに縛っていることがわかりました。

これはApplication.DoEventsがここ少なくとも、悪い選択肢で数回のいずれかである可能性があります

private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
{ 
    goOnPolling = false; 
    for(int i=0;i<10;i++) 
    { 
     Application.DoEvents(); 
     Thread.Sleep(50); 
    } 
    //foreach (Thread element in activeThreadlist) 
    // if (element.IsAlive) 
    //  element.Abort(); 
} 

もフォームがすでに持っていた後、彼らはInvokeしようとするとスローされた例外に対処するためのスレッドを変更します閉鎖されているのではなく、むしろAbortでそれらを引き裂こうとしています。


ので、多くのスレッドは、彼らがInvokeに欠けている状態になっている理由、それはこれまでの投稿コードから不明だについて - Invokeは、UIスレッドへのアクセスをシリアル化していることが、心の中でクマ。それは可能ですすべてのスレッドは、UIスレッドがに処理される前にInvokeに処理されています。スレッドへのアクセスが非常に輻輳している場合は、「フォームクローズ」イベントに変換されるウィンドウメッセージです。

関連する問題