2012-04-11 4 views
1

私は、完了したらUIを更新するバックグラウンドスレッドを持っています。私はできるだけ安全にしようとしていたので、私はDispositionされたGUIと呼んでいません。バックグラウンドスレッドからUIを更新する - GUIが削除されていないことをどのように知ることができますか?

void DoInBackground() 
{ 
    try 
    { 
     string result = ServerSideProcess(); 
*  if (!IsDisposed && !Disposing) 
*   BeginInvoke(new StringDelegate(UpdateText), result); 
    } 
    catch (Exception ex) 
    { 
*  if (!IsDisposed && !Disposing) 
*   BeginInvoke(new VoidDelegate(UpdateFailed)); 
    } 
} 


void UpdateText(string txt) 
{ 
    if (!IsDisposed && !Disposing) 
     textbox1.Text = txt; 
} 

void UpdateFailed() 
{ 
    if (!IsDisposed && !Disposing) 
     textbox1.Text = "failed to get data from server"; 
} 

override Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     if (components != null) 
      components.Dispose(); 
    } 
    base.Dispose(disposing); 
} 

私はGUIメソッドの内部に十分安全だと思います - 廃棄()私はUPDATETEXT(文字列)またはUpdateFailed(内てる間、呼び出されません)同じスレッドで、彼らの両方が実行されるため、だから、私はIsDisposingのチェックとそれ以降の実行が十分であると仮定している。しかし、(*)内のパーツがDispose()を間に合わないことをどのように知ることができますか?BeginInvokeは処理クラスで呼び出され、最終的にアプリケーションクラッシュが発生します。

Thread.Sleepの前と後にブレークポイントを入れ、BeginInvokeに達する前にDispose()dを得るようにコントロールから移動することで、(*)パーツ間にThread.Sleep(2000)を追加してテストしました。結果 - 私のアプリケーションがクラッシュしました。どのように私はこのランタイムは私にこの不運なコンテキスト切り替えのシナリオを与えてくれないことを知ることができますか?

答えて

2

私はそれを例外条件と呼びますが、BeginInvokesを捕まえて明示的にスローされる例外を処理するtry/catchを使用します。

+0

'InvokeRequired'、' BeginInvoke'などの方法には不幸な欠点があります。あなたの 'Dispose'ルーチン内でロックガードされたフラグを設定できない場合、レースフリーはありません'BeginInvokeUnlessWindowIsGone'を行う方法です。あなたは 'BeginInvoke'をして例外を飲み込むだけです。 – supercat

+0

@supercatが合意した、それは例外的なケースだと思うのです(そしてtry/catchは正当化されます)。 –

+0

発信側から、ええ。それはデザインは小さいですが、悪意のある点は、BeginInvoke(およびイベント)の多くが、呼び出し側ではなくターゲットの利益のために発生することをフレームワークが認識できないことに由来します。 (例えば、WeakEventまたはWeakDelegateがないために)同様の臭いがイベントで発生する。 – supercat

4

これは、Backgroundworkerによって無料で完全に解決されている問題のようです。

使用しない理由は何ですか?

+0

BackgroundWorkerはスレッドプールスレッドを使用しますが、そうではありませんか?いくつかのタスクは、多くの時間をブロックするため、または完了したときに消えるべきThreadStaticデータを使用するため、独自のスレッドを持つことでより効果的です。 – supercat

+0

プールから1つのスレッドを「盗む」ことは許容されます。とにかくThreadStatic/ThreadLocalは避けるべきです。 –

+0

1つのスレッドがプールから盗まれて長時間保持されても、それは問題にはなりませんが、(1)問題になる前に "バックアップされるためには、スレッドの要求が任意に遅れることがあります。 ThreadStaticについては素晴らしいことではありませんが、事態を最小限に抑える方法があります。 – supercat

0

次は私にすべてのエラーメッセージを表示し、実行時にしません:

BackgroundWorker bw = new BackgroundWorker(); 

public Form1() 
{ 
    InitializeComponent(); 

    bw.DoWork += bw_DoWork; 
    bw.RunWorkerCompleted += bw_RunWorkerCompleted; 

    Shown += Form1_Shown; 
} 

void Form1_Shown(object sender, EventArgs e) 
{ 
    bw.RunWorkerAsync(); 
    Thread.Sleep(1000); 
    Dispose(); 
} 

void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
    //This is your second thread. 
    Thread.Sleep(2000); 
} 

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    //This runs when the second thread is finished. Update here. 
    Text = "Done"; 
} 
+0

素晴らしい、それをテストし、それは実際に働いた、ありがとう! – user1327082

0

をいくつかのケースでは、あなたがコントロールがロックを獲得するためのDisposeルーチンを持つことにより、クリーンシャットダウンを達成することができるかもしれ、フラグを設定しました、ロックを解除します。更新ルーチンはロックを取得し、フラグが設定されていない場合はBeginInvokeを実行し、ロックを解除します。ロックの重要な目的は、更新ルーチンがBeginInvokeを呼び出すことを決定すると、コントロールが更新が行われるまで処理されないようにすることです。

多くの点で、私はそれがちょうどBeginInvokeを実行し、コントロールがあなたの下に配置された場合に発生する例外を飲み込むことがよりきれいだと思います。実際にはTryBeginInvokeがあり、BeginInvokeを返し、コントロールが生存していればTrueを返し、それ以外の場合はFalseを返します。ただし、コントロールのDisposeプロセスに自分自身を注入することなくレースフリーの方法もありませんまたは例外を発生させ、それを黙らせること。

関連する問題