2011-01-19 6 views
51


私は2つのコンボボックスを持つフォームを持っています。そして、combobox1.Textcombobox2.Textに基づいてcombobox2.DataSourceを入力したいと考えています(ユーザーがcombobox1に入力を完了し、入力中にcombobox2に入力しているとします)。だから私はこのようなcombobox2のイベントハンドラを持っている:BackgroundWorkerを正しく停止する方法

private void combobox2_TextChanged(object sender, EventArgs e) 
{ 
    if (cmbDataSourceExtractor.IsBusy) 
     cmbDataSourceExtractor.CancelAsync(); 

    var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text, 
     V2 = combobox2.Text}; 
    cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues); 
} 

限り建物のDataSourceが時間のかかるプロセス(それがデータベースへの要求を作成し、それを実行)であるように私はそれが別のプロセスでそれを実行する方が良いでしょうことを決めましたBackgroundWorkerを使用します。したがって、cmbDataSourceExtractorが作業を完了せず、ユーザーがもう1つのシンボルを入力するというシナリオがあります。この場合、BackgroundWorkerがビジーであり、同時に複数のアクションを実行することができないということについては、この行で例外が発生します。
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues);
この例外を取り除くにはどうすればよいですか?
ありがとうございます!

答えて

81

CancelAsync実際にあなたのスレッドなどを中止するものではありません。これは、ワーカースレッドに、BackgroundWorker.CancellationPendingで取り消すべきメッセージを送信します。バックグラウンドで実行されているDoWorkデリゲートは、定期的にこのプロパティをチェックし、キャンセル自体を処理する必要があります。

DoWorkデリゲートがブロックされている可能性があるというのは難しいことです.DelWorkデリゲートがブロックされている可能性があります。つまり、DataSourceでの作業が完了する必要があります(CancellationPendingのチェックなど)。あなたの実際の仕事を別の非同期の代理人に移す必要があるかもしれません(あるいはもっと良いことに、仕事をThreadPoolに提出する必要があるかもしれません)。そして、この内部ワーカースレッドが待機状態を引き起こすか、CancellationPendingを検出するまで、

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx

http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx

+1

DoAs()でCancellationPendningを正しく処理しても、DoAs()が数秒で終了するため、CancelAsyncを次の行に進めることはできません。 – dzendras

1

問題がcmbDataSourceExtractor.CancelAsync()cmdDataSourceExtractor.RunWorkerAsync(...) exitst、Cancel操作がまだ完了していない非同期メソッドであることに起因します。もう一度RunWorkerAsyncを呼び出す前に、cmdDataSourceExtractorが完了するのを待つ必要があります。これを行う方法はin this SO questionで説明されています。

24

あなたはそれがあなたの問題が解決されますので、同じようCancelAsync()とRunWorkerAsync()の間にループを追加する場合

private void combobox2_TextChanged(object sender, EventArgs e) 
{ 
    if (cmbDataSourceExtractor.IsBusy) 
     cmbDataSourceExtractor.CancelAsync(); 

    while(cmbDataSourceExtractor.IsBusy) 
     Application.DoEvents(); 

    var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text, 
     V2 = combobox2.Text}; 
    cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues); 
    } 

Application.DoEvents(への呼び出しでwhileループが)の実行をhaultますあなたの新しいワーカースレッドが現在のワーカースレッドが適切にキャンセルされるまで、あなたのワーカースレッドの取り消しを処理する必要があることに留意してください。以下のようなもので:でもキャンセルとcmbDataSourceExtractor.IsBusyプロパティを更新するので、あなたのGUIの処理を続行します最初のコードスニペットで

private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e) 
{ 
     if (this.cmbDataSourceExtractor.CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 
     // do stuff... 
} 

Application.DoEvents()(まだ処理されるメッセージキューをスレッドあなたの場合Applicationの代わりにcontinueを追加するだけです。DoEvents()ループはGUIスレッドをビジー状態にロックし、イベントを処理してcmbDataSourceExtractorを更新しません.IsBusy)

+0

WPFにApplication.DoEvents()がないため、このソリューションを使用できませんでした。私は新しい要求が到着したときに労働者がまだ忙しいときに追加のフラグを設定します。次にフラグが設定されると、RunWorkerCompletedコールバックからメインスレッドでRunWorkerAsyncを呼び出します。 – bor

0

私は同意します。しかし時にはもっと多くのものを追加しなければなりません。

IE

1)あなたは、このメソッドを呼び出す必要があり、このworker.WorkerSupportsCancellation = true;

2)以下のもの

public void KillMe() 
{ 
    worker.CancelAsync(); 
    worker.Dispose(); 
    worker = null; 
    GC.Collect(); 
} 

を行うには、あなたのクラスにいくつかのメソッドを追加しますが、そうする前に、あなたのアプリケーションを閉じます。

3)おそらくDispose, nullには、BackgroundWorkerの内部にあるすべての変数とタイマーを使用できます。

+2

こちらを参照してくださいBackgroundWorkerのDisposeを手動で呼び出す必要はありませんhttp://stackoverflow.com/questions/2542326/properway-to-dispose-of-a-backgroundworker –

+2

ちょうどgooglerに2回のデバッグ実行を保存するために、あなたの作業者がループを持っているか、またはRunWorkerCompletedEventHandlerを使用してキャンセルをチェックしている場合は、dispose、nullおよびCollectメソッドをRunWorkerCompletedEventHandlerに移動する必要があります。 – HockeyJ

+1

GC.Collect()は、アプリケーションのオブジェクトグラフ全体を積極的に通過するので決して呼び出さないでください。オブジェクトの数が多いアプリケーションのパフォーマンスが低下します。 – Vedran

4

MYの例です。 DoWorkは以下の通りです:DoLenghtyWork内部

DoLengthyWork(); 

    //this is never executed 
    if(bgWorker.CancellationPending) 
    { 
     MessageBox.Show("Up to here? ..."); 
     e.Cancel = true; 
    } 

:OtherStuff内部

public void DoLenghtyWork() 
{ 
    OtherStuff(); 
    for(int i=0 ; i<10000000; i++) 
    { int j = i/3; } 
} 

():あなたが何をしたいか

public void OtherStuff() 
{ 
    for(int i=0 ; i<10000000; i++) 
    { int j = i/3; } 
} 

はDoLenghtyWorkとOtherStuff(両方を変更している)彼らはなるように:

public void DoLenghtyWork() 
{ 
    if(!bgWorker.CancellationPending) 
    {    
     OtherStuff(); 
     for(int i=0 ; i<10000000; i++) 
     { 
      int j = i/3; 
     } 
    } 
} 

public void OtherStuff() 
{ 
    if(!bgWorker.CancellationPending) 
    { 
     for(int i=0 ; i<10000000; i++) 
     { 
      int j = i/3; 
     } 
    } 
} 
+4

私はこれが古いことを知っていますが、これは間違っています...仕事のどれかをする前にキャンセルが保留中であるかどうかチェックしていますか? –

0

私の場合、入金確認用のデータベースをプールしてから、WPF UIを更新する必要がありました。すべてのプロセスをスピンアップ

メカニズム:完了を確認するん

public void Execute(object parameter) 
     { 
      try 
      { 
       var amount = ViewModel.Amount; 
       var transactionId = ViewModel.TransactionMain.TransactionId.ToString(); 
       var productCode = ViewModel.TransactionMain.TransactionDetailList.First().Product.ProductCode; 
       var transactionReference = GetToken(amount, transactionId, productCode); 
       var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, transactionReference); 
       Process.Start(new ProcessStartInfo(url)); 
       ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true}; 
       ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork; 
       ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted; 
       ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync(); 
      } 
      catch (Exception e) 
      { 
       ViewModel.Log.Error("Failed to navigate to payments", e); 
       MessageBox.Show("Failed to navigate to payments"); 
      } 
     } 

メカニズム:

private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e) 
    { 
     Thread.Sleep(30000); 
     while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending) 
     { 
      Thread.Sleep(5000); 
     } 

     //Plug in pooling mechanism 
     this.AuthCode = GetAuthToken(); 
    } 

ウィンドウが閉じてしまった場合は取り消しメカニズム:

private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e) 
    { 
     var context = DataContext as PaymentViewModel; 
     if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy) 
      context.UpdateUiWhenDoneWithPayment.CancelAsync(); 
    } 
1

私の答えは少し違っていますが、私はこれらの方法を試しましたが、うまくいかなかったためです。私のコードは、データベースの値が読み込まれるとき、またはオブジェクトがListオブジェクトなどに追加される直前に好きな場所で、パブリック静的クラスのブール値フラグをチェックする余分なクラスを使用します。以下のコードの変更を参照してください。 ThreadWatcher.StopThreadプロパティを追加しました。この解説では、現在のスレッドを復活させるのはあなたの問題ではありませんが、次のスレッドにアクセスする前にプロパティをfalseに設定するのと同じくらい簡単です。今

private void combobox2_TextChanged(object sender, EventArgs e) 
{ 
    //Stop the thread here with this 
    ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped. 
    if (cmbDataSourceExtractor.IsBusy) 
     cmbDataSourceExtractor.CancelAsync(); 

    while(cmbDataSourceExtractor.IsBusy) 
     Application.DoEvents(); 

    var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text, 
     V2 = combobox2.Text}; 
    cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues); 
    } 

すべての罰金

private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e) 
{ 
     if (this.cmbDataSourceExtractor.CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 
     // do stuff... 
} 

データベース

List<SomeObject>list = new List<SomeObject>(); 
... 
if (!reader.IsDbNull(0)) 
    something = reader.getString(0); 
someobject = new someobject(something); 
if (ThreadWatcher.StopThread == true) 
    break; 
list.Add(something); 
... 

を読んでどこ使用することを忘れないでください

public static class ThreadWatcher 
{ 
    public static bool StopThread { get; set; } 
} 

と自分のクラスに次のクラスを追加あなたのデータを適切に閉じるためのfinallyブロックこれを助けることを願っています!あなたがそれが役に立つと分かったら、私に印を付けてください。

関連する問題