none
backgroundworkerで繰り返し処理を行うには RRS feed

  • 質問

  • こんにちは。

    VB2005+SQLServer2008 Expressを使用しております。

    プログレスバーで処理を進捗状況を表示するために、backgroundworkerを利用しております。
    (テキストファイルのレコードをDBにインポートする処理です。)

    複数のファイルの取り込みを連続して行うことを考えているのですが、backgroundworkerを複数用意する
    必要があるのでしょうか。

    それとも、何かほかの方法があるのでしょうか。

    プログレスバーの最小値と最大値のセットはbackgroundworkerを開始する前に行っているのですが、
    もし、ひとつのbackgroundworkerのみで蒸気の処理を行うことができるる場合、どのようなタイミングで
    2回目以降の処理のプログレスバーの最小値と最大値をセットすればよいかがわかりません。

    どうか、アドバイスをお願いします。


    • 編集済み TI-cb400 2012年4月28日 7:37
    2012年4月28日 5:44

回答

  • ファイル名の配列を渡せばよいだけでは?先に全部読み込む必要があるようには、今の文面からは見えません。
    また、ファイルを事前に全部読み込むと、データ量によってはメモリ不足になって破綻します。

    DoWork イベントから呼ばれる処理の組み方次第ですが、2 つアプローチが考えられます。

    1. DoWork イベントでは渡された 1 ファイルのみ処理し、RunWorkerCompleted イベントで残りのファイルがあれば次のファイルの準備をして、RunWorkerAsync メソッドを実行する。なければ終了する。
      RunWorkerCompleted イベントでファイル名の表示などの準備を行えばよく、スレッド間の排他も必要ではなく、今実現されている 1 ファイルでの処理を少し工夫するだけで実現できるので難易度は低い?
    2. DoWork イベントではファイル名の配列を受け取り、ループ構造にする。
      ループの序盤で ReportProgress でメインスレッドに更新を通知するが、進捗率ダミーの値とし、実際の値はフィールド変数に入れる。
      ProgressChanged イベントでフィールド変数から処理中のファイル名、処理済み件数などを見る。
      スレッド間で変数を共有することになるので、書き換えのタイミング、取得のタイミングで排他処理を実装する必要があるため、難易度がやや上がる。(排他しないと、ファイル名と件数が食い違うなど、問題が起きます)

    なお、前提としてワーカースレッドでファイルの読み込み、データベースへの登録が実装できるものとしておいています。
    もし、見えていない前提があるのであれば、きちんと出してください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年4月28日 22:56
    モデレータ

すべての返信

  • UIスレッドと別のスレッドで取り込みたいというのがbackgroundworkerを使う最大の理由だと思いますので、一つのbackgroundworkerで処理すれば目的が達せられると思います。このbackgroundworkerでデータベースにインポートする前に、処理する全てのテキストファイルを読んでインポートする合計の件数を求め、それを最大値にセットした後、データベースへインポートする処理を行えばよいように思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年4月28日 8:11
    モデレータ
  • ご回答ありがとうございます。

    テキストファイルは、1テーブル=1ファイルという形式になっており、イメージとしては、現在処理中の
    ファイル名、レコード件数、処理済の件数まで表示をしたいと考えております。

    また、上記のような形式のため、データによってはインポート先のテーブルも異なります。

    現在は、System.IO.File.ReadAllLinesを使用して、テキストファイルの内容を読み込み、フィールド
    ごとに分割をしてインポート処理をしております。

    trapemiya様がおっしゃる通り、すべてのテキストファイルを読み込んでしまえば、問題は解決しそう
    なので、考えてみました。

    たとえば、2次元配列にしてデータを取り込むことができれば、テーブルごとにデータを分けることができそうなので、
    上記のイメージを実現できるかな、と思いました。

    しかし、現在、ファイルの内容を取り込む際に使用しているSystem.IO.File.ReadAllLinesでは、2次元配列に
    格納するようなことができそうにありません。(私のヘルプを読む能力が足りないせいかもしれませんが)

    手動で配列に格納していくしか、方法はないものでしょうか。

    2012年4月28日 20:33
  • ファイル名の配列を渡せばよいだけでは?先に全部読み込む必要があるようには、今の文面からは見えません。
    また、ファイルを事前に全部読み込むと、データ量によってはメモリ不足になって破綻します。

    DoWork イベントから呼ばれる処理の組み方次第ですが、2 つアプローチが考えられます。

    1. DoWork イベントでは渡された 1 ファイルのみ処理し、RunWorkerCompleted イベントで残りのファイルがあれば次のファイルの準備をして、RunWorkerAsync メソッドを実行する。なければ終了する。
      RunWorkerCompleted イベントでファイル名の表示などの準備を行えばよく、スレッド間の排他も必要ではなく、今実現されている 1 ファイルでの処理を少し工夫するだけで実現できるので難易度は低い?
    2. DoWork イベントではファイル名の配列を受け取り、ループ構造にする。
      ループの序盤で ReportProgress でメインスレッドに更新を通知するが、進捗率ダミーの値とし、実際の値はフィールド変数に入れる。
      ProgressChanged イベントでフィールド変数から処理中のファイル名、処理済み件数などを見る。
      スレッド間で変数を共有することになるので、書き換えのタイミング、取得のタイミングで排他処理を実装する必要があるため、難易度がやや上がる。(排他しないと、ファイル名と件数が食い違うなど、問題が起きます)

    なお、前提としてワーカースレッドでファイルの読み込み、データベースへの登録が実装できるものとしておいています。
    もし、見えていない前提があるのであれば、きちんと出してください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年4月28日 22:56
    モデレータ
  • たとえば、2次元配列にしてデータを取り込むことができれば、テーブルごとにデータを分けることができそうなので、
    上記のイメージを実現できるかな、と思いました。

    私の書き方がまずく、書いたことがうまく伝わっていないように思いましたので補足します。私はプログレスバーにセットする最大値がわかれば良いだけかと思いましたので、例えば3ファイルあれば以下のようにしてデータベースへ保存する総合計数を求めるだけよいのではないかと思いました。

    1.Aファイルをオープンし、読み込む件数を取得し、Aファイルをクローズする。
    2.Bファイルをオープンし、読み込む件数を取得し、Bファイルをクローズする。
    3.Cファイルをオープンし、読み込む件数を取得し、Cファイルをクローズする。
    4.上記3ファイルの合計を求め、プログレスバーの最大値としてセットする。
    5.Aファイルをオープンし、データベースに保存する。
    6.Bファイルをオープンし、データベースに保存する。
    7.Cファイルをオープンし、データベースに保存する。

    例えば5以降をマルチスレッドで行うことも考えられますが、参照整合性などのため、データベースへ保存する順序が重要になる場合がありますので、注意が必要です。ただ、この部分をマルチスレッドにしてどの程度速度が向上するかは、環境(例えばSAS、SSD、SATAの違いなど)にもよるでしょうし、仕様にもよると思いますので、最初からマルチスレッドで設計する必要はないように思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年4月29日 3:04
    モデレータ
  • ご回答ありがとうございます。

    trapemiya様
    補足説明をありがとうございます。
    おっしゃる通り、勘違いをしておりました。

    Azulean様がご提示されたように、Completedイベントを利用することで、連続処理を実現することができました。

    このイベントを使うということは全く思いつきませんでした。

    本当にありがとうございました。

    また、困ったときは、なにとぞ、よろしくお願いします。

    2012年4月29日 12:01