トップ回答者
バックグラウンドワーカーについて

質問
-
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++]; ~~~データ処理~~~ }
回答
-
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
すべての返信
-
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