2009-08-06 24 views
1

私はタイマーとバックグラウンドスレッドのロジックを正しく取得しようとしています。私はすべての私の読書にもかかわらず、システム全体を完全に理解することはできません。以下は、関係のコードの抜粋です: 私のポーリングボタン:BackgroundWorkerスレッドとタイマーロジック

私は右、これまで

私のバックグラウンドワーカースレッドだと思う

private void pollStart_Click(object sender, EventArgs e) 
    { 
     tst_bgw = new BackgroundWorker(); 
     //mandatory. Otherwise will throw an exception when calling ReportProgress method 
     tst_bgw.WorkerReportsProgress = true; 
     //mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation 
     tst_bgw.WorkerSupportsCancellation = true; 
     tst_bgw.DoWork += tst_bgw_DoWork; 
     tst_bgw.ProgressChanged += tst_bgw_ProgressChanged; 
     tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted; 
     tst_bgw.RunWorkerAsync(); 

    } 

private void tst_bgw_DoWork(object source, DoWorkEventArgs e) 
    { 
     m_timer = new System.Timers.Timer(); 
     m_timer.Interval = 1000; 
     m_timer.Enabled = true; 
     m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); 
     if (tst_bgw.CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 

    } 

と経過ティアイベントコード:

private void OnTimedEvent(object source, ElapsedEventArgs e) 
    {    
     if (powerVal > 3250) 
     { 
      m_timer.Stop(); 
      tst_bgw.CancelAsync(); 
     } 
     else 
     { 
      string pow;     
      int progressVal = 100 - ((3250 - powerVal)/timerVal); 
      uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it. 

      pow = construct_command("power", powerVal); 
      sData = Encoding.ASCII.GetBytes(pow); 

      if (active_connection) 
       try 
       { 
        m_sock.Send(sData); 
        Array.Clear(sData, 0, sData.Length); 
        tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK. 
        m_sock.Send(time_out_command); 
        tstDel(ref unit_Output); 
        tst_bgw.ReportProgress(progressVal); 
       } 
       catch (SocketException se) 
       { 
        MessageBox.Show(se.Message); 
       } 
      tst_bgw.ReportProgress(powerVal, progressVal); 
      powerVal = powerVal + pwrIncVal; 
     } 

私はほんの少しの他の薄いgs;私は正しいタイマーを使用しています(大したことだとは思いませんが、これは私がやりたいことのための最良のタイマーかもしれないと示唆されていました)。そして、DoWorkメソッドのUI要素を代理人によってのみ変更することができます。そうすることには重大な考慮が必要です。 長い投稿をお詫びし、お時間をありがとうございます。

+0

私の心配は、あなたのタイマーがOnTimerEventを呼び出す実行中にdoWorkプロシージャが終了する(workCompleteに進む)ことです。あなたはちょうどスレッドでループをしたくないと確信していますか? – automatic

+0

いいえできません。ソフトウェアを動かすロジックは、毎秒何らかの処理を行う必要があります –

答えて

1

このコードには多くの誤りがあります。

1)あなたはバックグラウンドワーカーを処分していません。 BackgroundWorkersは使用後に処分する必要があります。それらはwinformsコンポーネントとして使用するように設計されており、通常はデザイナーを介してウィンドウに追加されます。これにより、フォームとともに作成され、フォームが廃棄されたときに処分されます。
2)あなたのdoworkメソッドでやっていることは、新しいタイマーを作成して実行していることだけです。とにかくそれがすぐに起こるため、バックグラウンドワーカーでこれを行うということはありません。
3)バックグラウンドワーカーを再度実行するたびに、タイマーを再作成します。しかし、古いタイマーを止めたり廃棄したりすることはありません。メンバを上書きするだけです。

BackgroundWorkerを完全に削除し、タイマーを使用することをお勧めします。フォームコンストラクタにタイマーを作成し、それをフォームのdisposeメソッドで廃棄してください。 (またはデザイナーを使用してフォームに追加します)。 pollstart_clickメソッドでは、タイマーを開始するだけです。 (ポーリングを停止する方法がある場合は、その中でタイマーを停止することができます)

+0

何か不足していますか?私がコードの見たところでは、タイマーは範囲外になることはできません(したがって、収集できません)。 *親オブジェクトのメンバです(フォーム?) *イベントリスナーが経過イベント – jeroenh

+0

@ジェーレン:はい、申し訳ありませんが、あなたは正しいです。 (編集する)。私はバックグラウンドワーカーの方法で作成されたという事実を見ていました。メソッドが再度実行された場合、古いタイマーをクリアせずに新しいタイマーが作成されます。 –

+0

あなたの洞察をお寄せいただきありがとうございます。申し訳ありませんが、コード全体を投稿していないので、長すぎると思ったので、ちょうどその部分を取りました。 Simon私のGUIが大きく遅れているのでタイマーを取り除くことはできません。これは、以前の投稿では、インタフェースの遅れについてバックグラウンドワーカーを使うことを提案しています。私は論理を構築する方法を知りませんでした。 (私がここでやろうとしている)BackgroundworkerスレッドでTimerスレッドを呼び出して作成するか、タイマースレッド内でBackgroundworkerスレッドを作成するかどうかを指定します。 –

1

目的を達成するためにBackgroundWorkerとタイマーの両方を使う必要はありません。あなたが掲示したものから、ユーザーが証明書のポイントで終了するポーリングプロセスを開始するボタンをクリックさせるように見えるように見えます。

あなたのポーリングモデルは、タイマーが正常に動作することを実際に示唆しています。

あなたはタイマーを使用している場合は、私は単に

private void button_Click(object sender, EventArgs e) 
{ 
    this.timer.Start(); 
} 

その後timer_Tickにあなたが行う必要がありますでしょうbutton_clickを)のInitializeComponent(後にタイマーを初期化し

private void InitializeTimer() 
{ 
    this.timer = new Timer(); 
    int seconds = 1; 
    this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds 
    this.timer.Tick += new EventHandler(timer_Tick); 
    // don't start timer until user clicks Start 
} 

のようなものを呼ぶだろうあなたのポーリングと、このようなUIスレッドにタイマーがある場合は、そこからUIを更新できるはずです

void timer_Tick(object sender, EventArgs e) 
{ 
    if(determineIfTimerShouldStop()) 
    { 
     this.timer.Stop(); 
    } 
    else 
    { 
     // write a method to just get the power value from your socket 
     int powerValue = getPowerValue(); 

     // set progressbar, label, etc with value from method above 
    } 
} 

しかし、タイマースレッドがUIと同じスレッドにない場合は、UIを更新しようとすると例外が発生します。その場合、あなたはDataDinkが言及していることを呼び出しを使用して、あなたが本当にBackgroundWorkerのとタイマーの両方を行う必要はありませんでしたが、私はどこのインスタンスを持っていた投稿のコードを考えると、この

void timer_Tick(object sender, EventArgs e) 
{ 
    if(determineIfTimerShouldStop()) 
    { 
     this.timer.Stop(); 
    } 
    else 
    { 
     // write a method to just get the power value from your socket 
     int powerValue = getPowerValue(); 

     // set a label with Invoke 
     mylabel.Invoke( 
      new MethodInvoker(delegate { mylabel.Text = "some string"; }) 
        ); 
    } 
} 

ような何かを行うことができますBackgroundWorkerを使用してタイマーが呼び出されたときに作業を行い、タイマーの更新UIを定期的に持ち、UIを更新する手動ボタンがあるようにしました。しかし、私は自分のUIをまったく更新していませんでした。

あなたはまだ両方を行う必要がある場合は、ここで

  • ...あなたのアプリを流れることができるか、おおよそ、であるあなたが を持っているので、InitializeTimerとともに InitailizeBackgroundWorker()メソッド を作成します。 タイマーが起動する前に既に初期化されています。
  • が続い BackgroundWorker.ReportProgressを使用して、あなたが によってRunWorkerAsync内からすべてのUIの更新を行うことができます BackgroundWorker.RunWorkerAsync()
  • を呼び出すためにTimer.Tick を設定します()。
関連する問題