2017-08-21 18 views
2

私が必要とするすべてのプロセスが操作されるWinFormで作業しています。今では、BackgroundWorkerProgressBarと統合し、キャンセルボタンを自分のコードに組み込もうとしています。別の方法ではなく、自分のコードの周りにローカルに置いて欲しい。これをテストするために、進行中のバー(まだアクティブではない)とfor-loopを停止するボタンで新しいフォームが作成されます。ただし、コードは機能していません(プログレスバーはまだ含まれていません)。フォームはすぐにフリーズし(画像参照)、キャンセルボタンをテストすることはできません。ただしforループは実行され、"Done: " + l.ToString()が表示されます。これをどうすれば解決できますか?BackgroundWorkerを使用したフォームのフリーズ

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true;  
} 

public void testcancel() // Test method which is triggered manually 
{ 
    int l = 0; 

    MetingProgress metingProgress = new MetingProgress(); 
    metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement); 

    BackgroundWorker worker = new BackgroundWorker(); 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += (sender, args) => 
    {      
     for (int k = 0; k < 10; k++) 
     { 
      Thread.Sleep(1000); 
      l++; 

      if (worker.CancellationPending) 
       break; 
     } 

     MessageBox.Show("Done: " + l.ToString()); 

    }; 
    worker.RunWorkerAsync(); 

    while (worker.IsBusy) 
    { 
     if (stopMeas) 
      worker.CancelAsync(); 
    } 

    metingProgress.Dispose(); 
    MessageBox.Show("All done"); 

} 

enter image description here

+0

はい、私はすでに私の例のような設定をしたいので、backgroundworkerに入る必要があるいくつかのタスクのための私のコードを持っています。 forループは、実行するタスクに似ています。 – 10a

+0

私は、 'testcancel'は実際には単にスレッドを起動してバックグラウンドで実行させる方法であり、実際にはそれがすべてです。スレッドは長い時間実行されるので、このメソッドではフォームの破棄を処理しないでください!それはスレッドメソッドの仕事だろう。 'MessageBox.Show(" All done ");'行は 'DoWork'イベントに属します。なぜなら、実際にスレッドが開始されるメソッドではなく、いつジョブ自体が完了するのかを知っているからです。 –

答えて

4

フォームは、ループはまだメインスレッドで実行している間、あなたはを持っているので、これはすぐに

をフリーズ!したがって、フォームは反応しません。これは、buisy waitingと呼ばれています。 CancelAsyncメソッドを呼び出すことはできません。であるあなたは基本的に行っている何

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true; 
    worker.CancelAsync(); 

} 

:あなたは、第二のキャンセルトークンを作成し

一つの解決策は、whileループを削除し、ボタンイベントコードにキャンセル電話をかけることができます。

worker.DoWork += (sender, args) => 
{      
    for (int k = 0; k < 10; k++) 
    { 
     Thread.Sleep(1000); 
     l++; 

     if (stopMeas) 
      break; 
    } 

    string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!"; 
    MessageBox.Show(mes); 

}; 

EDIT:この行:

metingProgress.Dispose(); 

がObjectDisposed例外につながる可能性があるので、別の可能性は、バックグラウンド操作をキャンセルするだけstopMeasを使用することができます。バックグラウンドプロセスがまだ実行中で、プログレスバーを更新しようとしていて、すでにフォームを破棄している場合。この行を削除してガベージコレクタに残す必要があります。

+0

ループを削除しますか?しかし、労働者の中に何も残っていないのですか? – 10a

+0

彼は 'while(worker.IsBusy) 'ループについて話しています。 – Fildor

+0

はい、私はそれを見て、それは今働いています。 – 10a

4

このコードは、あなたの問題である:あなたの労働者が完了するまで

while (worker.IsBusy) 
{ 
    if (stopMeas) 
     worker.CancelAsync(); 
} 

あなたのGUIスレッドは、そのループです。 あなたは、あなたのワーカー・インスタンスをEventHandler内から到達可能にして、そこからworker.CancelAsync()を呼び出す必要があります。この外


、私は個人的に2つの段階でコードを改善する:

  1. はMetingProgressクラスに全体のBackgroundWorkerを移動して、そのコンストラクタは、実際の作業の実施のためのデリゲートを取らせ

  2. TAP(タスク非同期パターン)を使用します。つまり、async/progressとCancellationTokenでタスクを待機します。

+0

私は展開しようとしています。私のコードを段階的に理解し、それがどのように正確に機能するかを理解するために、おそらく私はあなたが提案しているもので終わるでしょう。 – 10a

+2

TAPへの移行には、あなたの頭を包み込むのに本当に時間が必要です。あなたの時間を取るが、それは間違いなく試してみる価値がある。それを一歩一歩進めることが、あなたが学ぶためにできることは最高です。頑張って! – Fildor

関連する問題