2016-09-06 6 views
2

でプロセス中の実装を停止する方法です。このコードは、ランダムな開始番号を持つインクリメントされた数値ストリームの値をとります。 start1の値が大きく、start2の場合は、対応する行をテキストボックスで表示します。backgroundWorker

問題指定されたサイクル数が満たされなくなるまで、プログラムを停止できません。ボタンは移植中にハングします。私は、これが起こる理由がループであることを理解しています。今私はbackgroundWorkerで停止しようとしていますが、同じ結果が得られ、キャンセルボタンも同じ方法で停止します。

backgroundWorker1_ProgressChanged内にあるbackgroundWorkerで使用したいコードです。私はここで何が起こっているのか、本当に分かりません。たぶん、あなたは私が間違ってやっているかを把握するために私を助ける:

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 

namespace XX_8_0 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
      backgroundWorker1.WorkerReportsProgress = true; 
      backgroundWorker1.WorkerSupportsCancellation = true; 
     } 

     private void startAsyncButton_Click(object sender, EventArgs e) 
     {  
      if (backgroundWorker1.IsBusy != true) 
      { 
       backgroundWorker1.RunWorkerAsync(); 
      } 
     } 

     private void cancelAsyncButton_Click(object sender, EventArgs e) 
     { 
      if (backgroundWorker1.WorkerSupportsCancellation == true) 
      { 
       backgroundWorker1.CancelAsync(); 
      } 
     } 

     private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker worker = sender as BackgroundWorker; 

      for (int i = 1; i <= 10; i++) 
      { 
       if (worker.CancellationPending == true) 
       { 
        e.Cancel = true; 
        break; 
       } 
       else 
       { 
        System.Threading.Thread.Sleep(500); 
        worker.ReportProgress(i * 10); 
       } 
      } 
     } 

     private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      var random = new Random(); 
      var start1 = random.Next(0, 100); 
      var start2 = random.Next(0, 100); 
      var incrementor1 = start1 > 50 ? -1 : 1; 
      var incrementor2 = start2 > 50 ? -1 : 1; 
      var cV1 = start1; 
      var cV2 = start2; 

      for (var i = 0; i < 1000; i++) 
      { 
       if (cV1 == 101) incrementor1 = -1; 
       if (cV1 == 0) incrementor1 = 1; 

       if (cV2 == 101) incrementor2 = -1; 
       if (cV2 == 0) incrementor2 = 1; 

       if (cV1 > cV2) 
       { 
        textBox1.AppendText("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n"); 
       } 
       else 
       { 
        textBox1.AppendText("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n"); 
       } 

       cV1 += incrementor1; 
       cV2 += incrementor2; 
      } 
     } 
     private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      if (e.Cancelled == true) 
      { 
       resultLabel.Text = "Canceled!"; 
      } 
      else if (e.Error != null) 
      { 
       resultLabel.Text = "Error: " + e.Error.Message; 
      } 
      else 
      { 
       resultLabel.Text = "Done!"; 
      } 
     }  
    } 
} 
+0

私の答えに加えて、あなたは役に立つ情報のためにそのページの** Related **セクションを見るかもしれません。明らかに、上記の実装は非常に悪く、 'BackgroundWorker'がどのように動作するかについての貧弱な理解を示しています。 – Phil1970

答えて

3

:私はあなたの例では、本当に多分BackgroundWorkerは何ができるかの良い例ではないと思います。本質的にあなたのコードはちょっと眠ってしまい、進行状況を報告し、別のスレッドではほとんど役に立たない。


あなたを追加するので、多少時間がかかることがありますとかなり重いです。

ボタンは移植中にハングします。私は、これが起こる理由がループであることを理解しています。

あなたはbackgroundWorker1_ProgressChangedは、UIスレッド上で実行考えると、関係なく、あなたがに作るクリックの何番目backgroundWorker1_ProgressChanged戻るまで処理されませんキャンセルありません。すべてのUI処理は、UIスレッドによって行われなければなりません。この間、バックグラウンドワーカースレッドも中断されていることを指摘しておきましょう。残念ながら、キャンセルをテストするのはコードの唯一の部分です。 ReportProgressが非同期であっても、キャンセルボタンのクリックイベントを処理して作業をCancellationPendingとマークする必要があります。

私はあなたがbackgroundWorker1_ProgressChangedに一度のように多くを報告したり、おそらくtextBox1としてバッチに項目を追加することの報告を考慮していないお勧めします。

このようにすれば、メッセージポンプとアプリケーションの応答性が向上します。

はあなた backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) バッチを使用するように変更し

として:あなたのProgressChangedハンドラが1000倍コールReportProgressあたり約UIを更新する非常に忙しいですので

var builder = new StringBuilder(); 

for (var i = 0; i < 1000; i++) 
{ 
    if (cV1 == 101) incrementor1 = -1; 
    if (cV1 == 0) incrementor1 = 1; 

    if (cV2 == 101) incrementor2 = -1; 
    if (cV2 == 0) incrementor2 = 1; 

    if (cV1 > cV2) 
    { 
     builder.Append("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n"); 
    } 
    else 
    { 
     builder.Append("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n"); 
    } 

    cV1 += incrementor1; 
    cV2 += incrementor2; 
} 

textbox1.AppendText(builder.ToString()); 
+0

よく私は非常に悪いパフォーマンスを得て、本当に何をすべきかわかりません。_worker.ReportProgress_を介して私のコードを_backgroundWorker1_DoWork_に渡すと、エラーが発生します。 –

+0

私はそれを非常に疑います。あなたはもっと速くなると思いますか?単一の 'AppendText()'または** 1000 x ** 'AppendText()'?コールスタックが別のスレッド(バックグラウンドワーカーの 'ReportProgress')からのものであっても、CPU時間の大部分は' AppendText() 'に費やされています。 – MickyD

0

が、それはすべてのUIスレッドのCPU時間を使用すると、そのスレッドがしますすでにキャンセルボタンが処理されていないので、キャンセルボタンを処理することはできません。

BackgroundWorkerを使用すると、ハンドラ内のほとんどのProgressChangedを移動し、バックグラウンドスレッドでそのスリープを削除する必要があります。

ところで、あなたはBackgroundWorkerを使用しても意味がありません。 UIスレッドは可能な限り小さくする必要があり、バックグラウンドスレッドは可能な限り多くする必要があります。そして、バックグラウンドの仕事は経過した時間に依存しないので、あなたのケースではバックグラウンドスレッドで眠るのが理にかなっていません。

コードを移動したら、データをUIスレッドに送り返す必要があります。しかし、これは非常に簡単です.のオーバーロードがあります.OutオブジェクトをUIスレッドに渡すことができます(一般的にUIスレッドからバックグラウンドワーカーが作成されたと仮定します)。反対側では、ユーザーデータを適切な型に単純にキャストし、その情報を使用して適切なテキストをテキストボックスに追加します。

改善されたコードでは、そのデータをより迅速に処理できるはずです。ところで、StringBuilderも文字列を作成するために使用する必要があります。実際、UIテキストを1000回更新する頻度が少なくなるため、UIパフォーマンスに劇的な影響を与えるはずです。