none
バックグラウンドワーカーについて RRS feed

  • 質問

  • VS2008Expressを使用しています。
    現在、バックグラウンドワーカーを用いた処理を作成しているのですが、不可解な動きを
    しているのでお尋ねします。

    タイマー処理でデータを取り込み、バックグラウンドワーカーでその取り込みを検知した
    らプログレス処理でそのデータを吸い上げてコントロールに描画といった処理です。
    (バックグラウンドワカー処理内ではコントロールにアクセスできないですよね?)

    下記のようなコードですが、タイマーでのデータ取り込みが進んでないのに、なぜかプロ
    グレスが次から次へ来てしまうという現象が起きています。
    複雑な処理でないのに、なぜこのような動きをするのか悩んでいます。
    何が悪いのか教えてもらえないでしょうか。

    よろしくお願いします。
    float[] Value = new float[1000];							//! データスプールバッファ
    int Index;													//! データ取り込みインデックス
    int pIndex;													//! データ処理インデックス
    private void mStart_Click( object sender, EventArgs e )
    {
    	Index = pIndex = 0;
    	backgroundWorker1.RunWorkerAsync();						//! スレッド処理開始
    	
    	timer1.Interval = 100;									//! インターバル設定
    	timer1.Start();											//! タイマー開始
    }
    
    //! タイマーイベント
    private void OnTimer( object sender, EventArgs e )
    {
    	~~~データ取り込み~~~
    	Value[Index++] = value;
    }
    		
    //! バックグラウンド処理
    private void DoWork( object sender, DoWorkEventArgs e )
    {
    	while ( backgroundWorker1.CancellationPending == false ) {	//! スレッドキャンセル無し
    		if ( pIndex != Index ) {
    			backgroundWorker1.ReportProgress( 0 );
    		}
    	}
    }
    
    //! スレッド進捗
    private void Progressed( object sender, ProgressChangedEventArgs e )
    {
    	float value = Value[pIndex++];
    	~~~データ処理~~~
    }
    
    
    2010年1月21日 7:04

回答

  • DoWork イベントハンドラと ProgressChanged イベントハンドラは非同期なので、ReportProgress を呼び出したあと、Progressed が呼び出されたかどうかは気にせず、DoWork は次の処理を始めます。Progressed が呼び出される前に pIndex と Index が比較されると当然まだ pIndex が更新前なので再び ReportProgress が呼び出されてしまいます。

    こういう形式の場合、Queue を使うのが一般的です。
    Queue<float> を用意して、データ取り込み時には Enqueue して取り込んだデータをキューに追加。
    DoWork イベントハンドラ内では、Queue の中身が 1 つ以上入っているかどうかを確認して、入っていれば Dequeue でデータを取り出して ReportProgress する。
    ReportProgress は引数を 2 つ取るオーバーロードがあるので、この第二引数に取り出したデータを渡すのが便利です。この第二引数で渡されたデータは、ProgressChanged イベントハンドラの方では ProgressChangedEventArgs である e の UserState で取得できます。

    というか、この構造だとわざわざ BackgroundWorker を使う必要が感じられませんが……。
    • 回答としてマーク shimpo 2010年1月21日 7:41
    2010年1月21日 7:40

すべての返信

  • DoWork イベントハンドラと ProgressChanged イベントハンドラは非同期なので、ReportProgress を呼び出したあと、Progressed が呼び出されたかどうかは気にせず、DoWork は次の処理を始めます。Progressed が呼び出される前に pIndex と Index が比較されると当然まだ pIndex が更新前なので再び ReportProgress が呼び出されてしまいます。

    こういう形式の場合、Queue を使うのが一般的です。
    Queue<float> を用意して、データ取り込み時には Enqueue して取り込んだデータをキューに追加。
    DoWork イベントハンドラ内では、Queue の中身が 1 つ以上入っているかどうかを確認して、入っていれば Dequeue でデータを取り出して ReportProgress する。
    ReportProgress は引数を 2 つ取るオーバーロードがあるので、この第二引数に取り出したデータを渡すのが便利です。この第二引数で渡されたデータは、ProgressChanged イベントハンドラの方では ProgressChangedEventArgs である e の UserState で取得できます。

    というか、この構造だとわざわざ BackgroundWorker を使う必要が感じられませんが……。
    • 回答としてマーク shimpo 2010年1月21日 7:41
    2010年1月21日 7:40
  • そういう事でしたか。ReportProgressはあくまで要求を出すだけなんですね。
    それなら納得です。動きの原因がわかりました。
    Queueは使った事がないですが、やってみます。
    ありがとうございました。
    2010年1月21日 8:04