2009-05-05 12 views
4

C#2008C#バックグラウンドワーカーのDoWorkをキャンセルする

ソフトフォンにログインするには、以下のコードを使用しています。しかし、初期化してチェックする必要があることが多いので、ログイン処理は長いプロセスですが、ここにはいくつかを載せておきます。

以下のコードでは、各チェックを行う前にCancelAsyncがキャンセルボタンのクリックイベントで呼び出された場合、CancellationPendingがチェックされています。これは正しいです?また、チェックが失敗した場合は、CancelAsyncを呼び出してe.Cancelをtrueに設定します。

私がここで使用した方法が使用するのが最良の方法であるかどうかを知りたいと思います。何かアドバイスのため

多くのおかげで、

private void bgwProcessLogin_DoWork(object sender, DoWorkEventArgs e) 
    { 
     /* 
     * Perform at test to see if the background worker has been 
     * cancelled by the user before attemping to continue to login. 
     * 
     * Cancel background worker on any failed attemp to login 
     */ 

     // Start with cancel being false as to reset this if cancel has been set to true 
     // in the cancel button. 
     e.Cancel = false; 

     NetworkingTest connection_test = new NetworkingTest(); 
     if (!this.bgwProcessLogin.CancellationPending) 
     { 
      // Check local LAN or Wireless connection    
      if (!connection_test.IsNetworkConnected()) 
      { 
       // Update label 
       if (this.lblRegistering.InvokeRequired) 
       { 
        this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "No network connection"); 
       } 
       else 
       { 
        this.lblRegistering.Text = "No network connection"; 
       } 
       // Failed attemp 
       this.bgwProcessLogin.CancelAsync(); 
       e.Cancel = true; 
       return; 
      } 
      // Report current progress 
      this.bgwProcessLogin.ReportProgress(0, "Network connected"); 
     } 
     else 
     { 
      // User cancelled 
      e.Cancel = true; 
      return; 
     } 

     // Test if access to Server is available 
     if (!this.bgwProcessLogin.CancellationPending) 
     { 
      if (!connection_test.IsSIPServerAvailable()) 
      { 
       // Update label 
       if (this.lblRegistering.InvokeRequired) 
       { 
        this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "Server unavailable"); 
       } 
       else 
       { 
        this.lblRegistering.Text = "Server unavailable"; 
       } 
       // Failed attemp 
       this.bgwProcessLogin.CancelAsync(); 
       e.Cancel = true; 
       return; 
      } 
      // Report current progress 
      this.bgwProcessLogin.ReportProgress(1, "Server available"); 
     } 
     else 
     { 
      // User cancelled 
      e.Cancel = true; 
      return; 
     } 
     . 
     . 
     . 
} 


private void bgwProcessLogin_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     // Check for any errors 
     if (e.Error == null) 
     { 
      if (e.Cancelled) 
      { 
       // User cancelled login or login failed     
      } 
      else 
      { 
       // Login completed successfully     
      } 
     } 
     else 
     { 
      // Something failed display error 
      this.statusDisplay1.CallStatus = e.Error.Message; 
     } 
    } 


private void bgwProcessLogin_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     this.lblRegistering.Text = e.UserState.ToString(); 
    } 

private void btnCancel_Click(object sender, EventArgs e) 
    { 
     // Cancel the logging in process 
     this.bgwProcessLogin.CancelAsync(); 
     this.lblRegistering.Text = "Logged out"; 
} 

答えて

8

DoWorkイベントハンドラの操作の1つが長時間続く場合は、問題が1つだけである可能性があります。この場合、その操作が完了した後にのみ保留中の操作を中止することができます。 DoWorkイベントのすべての操作が非常に長く続くことができない場合(たとえば、5秒以下)、すべてOKですが、この場合、操作の1つが長い時間(たとえば5分)持続できる場合、ユーザーはこの操作が完了するまで待つ。

DoWorkに長期間の操作が含まれる場合、AbortableBackgroundWorkerのようなものを使用できます。このような何か:この場合

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; 
     } 
    } 
} 

あなたが保留中の操作を本当に中止することができますが、あなたはまた、(マネージスレッドを中断し、いくつかの制限がPlumbing the Depths of the ThreadAbortException Using Rotorを参照してください詳細については)いくつかの制限があります。

P.S.私はOliverに、InvokeRequiredをもっと使いやすい形で包むべきだと同意します。

+0

ニースの回答。私はこれがSilverlightでもうまくいくことを期待していました。それはセキュリティの制限のためではないことが分かります。 Silverlight 4(http://msdn.microsoft.com/en-us/library/ty8d3wta(v=VS.95).aspx)の時点で、 'Thread.Abort()'を呼び出すとMethodAccessExceptionがスローされます。ああ、それはまだ良い答えです。 –

+2

@ SergeyTeplyakovこんにちはSergey、私はまったく同じシナリオを持っています(キャンセルがクリックされた場合、中止したい1つの長い実行スレッド)が、あなたのコード/ポストから実際にキャンセルする方法は見ていません。私はバックグラウンドワーカーには新しいので、私の無知を許してください... – ganders

1

あなたはそれを正しい方法を行っているが、私は信じています。あなたは、あなたがスレッドを終了または中止することを可能にするスレッドメンバーを見つけるでしょうが、あなたはこのようなもののためにそれらを使いたくありません。あなたのコードですべての「取り消された」チェックを持つのはちょっと変わって見えるかもしれませんが、スレッドを終了するタイミングを正確に制御することができます。もしあなたがワーキングスレッドを「不当に」中止すると、そのスレッドはいつ終了したのかを制御することができず、破損した状態になる可能性があります。

0

this.bgwProcessLogin.CancelAsync()を呼び出す必要はありません。このe.Cancel = trueを設定するだけで済みます。

1

DoWork()関数内では、...と書きました。同じ構造のタスクの数が表示された2つのもののようになっているかどうかに応じて、この構造を独自のメソッドにリファクタリングして、変化する部分をパラメータとして与えることができます。

また、このInvokeRequired if-elseブランチは出力文字列を2倍にしました。 stackoverflowまたはWeb上で少し検索すると、この倍増を達成するパターンが表示されます。

他のすべてのものはかなり良いです。

関連する問題