.NET TIPS

時間のかかる処理をバックグラウンドで実行するには?[2.0のみ、C#、VB]

デジタルアドバンテージ 遠藤 孝信
2006/04/14

 Windowsアプリケーションでは、時間のかかる処理(以下、重い処理)はアプリケーションのメインのスレッドとは別のスレッドで行わなければならない。これは重い処理がアプリケーションのユーザー・インターフェイス(UI)をフリーズさせてしまうからだ。

 たとえユーザーがその処理の完了まで待つ必要があるとしても、その間にウィンドウの移動や最小化ができなければならないし、可能であれば処理の進ちょく状況をプログレス・バーなどで表示すべきである。

 .NET Framework 2.0では、そのような処理を容易に実装するための「BackgroundWorkerコンポーネント」が新しく追加されている。これを使えばスレッドをほとんど意識することなく、重い処理を別スレッド(バックグラウンド)で実行させることができる。

 本稿では、BackgroundWorkerコンポーネントを利用し、進ちょく状況を表示しながら、重い処理をバックグラウンドで実行するための方法について解説する。

 なおBackgroundWorkerコンポーネントでは、バックグラウンドの処理をキャンセルして途中で中止することもできる。これについては別稿「TIPS:バックグラウンド処理を途中でキャンセルするには?」で解説しているので併せて参照していただきたい。

BackgroundWorkerコンポーネントの配置

 以下では、Visual Studio 2005でサンプル・アプリケーションを作成しながらBackgroundWorkerコンポーネントの使い方について説明していく。

 まず、Windowsアプリケーションのプロジェクトを新規作成し、以下の画面のように[スタート]ボタンとProgressBarコントロール、そしてBackgroundWorkerコンポーネントを配置する。そしてプロパティ・ウィンドウで、それぞれのNameプロパティを「startButton」「progressBar」「bgWorker」に設定する。


BackgroundWorkerコンポーネントを配置したWindowsフォーム
進ちょく状況を表示するためには、BackgroundWorkerコンポーネントのWorkerReportsProgressプロパティをtrueに設定しておく。

 さらにBackgroundWorkerコンポーネントについては、進ちょく状況を表示するためにWorkerReportsProgressプロパティをtrueに設定する。

 このサンプル・プログラムでは、[スタート]ボタンがクリックされると、重い処理を開始し、その進ちょく状況をProgressBarコントロールのバーと、タイトルバーの文字で示す。また、処理完了時にはメッセージボックスを表示する。

BackgroundWorkerコンポーネントの動作

 BackgroundWorkerコンポーネントに対しては、次の3つのイベント・ハンドラの追加が必要となる(詳細は割愛するが、イベント・ハンドラの追加はC#ではプロパティ・ウィンドウから、VBではコード画面上部から簡単に行える)。

重い処理を行うDoWorkイベント・ハンドラ(別スレッドで実行される)
進ちょく状況を表示するためのProgressChangedイベント・ハンドラ
処理完了時に実行されるRunWorkerCompletedイベント・ハンドラ

 重い処理の実行開始から処理完了までの流れは次のようになる。

  1. BackgroundWorkerコンポーネントのRunWorkerAsyncメソッドを呼び出すことにより、 が実行される。このときにパラメータを渡せば、 では「e. Argument」*1として受け取ることができる。

  2. の重い処理の間にBackgroundWorkerコンポーネントのReportProgressメソッドを適時呼び出すことにより、 が実行される。メソッドのパラメータには進ちょく状況を示す整数値を指定する。 では「e. ProgressPercentage」*2により、この値を受け取ることができる。

  3. の重い処理が完了すれば(イベント・ハンドラの実行が終了すれば)、 が実行される。 で処理結果を「e.Result」*3に代入しておけば、 で「e.Result」*4によりその値を受け取ることができる。

*1 「e」はDoWorkEventArgsクラスのオブジェクト。
*2 「e」はProgressChangedEventArgsクラスのオブジェクト。
*3 「e」はDoWorkEventArgsクラスのオブジェクト。
*4 「e」はRunWorkerCompletedEventArgsクラスのオブジェクト。
※ いずれのクラスもSystem.ComponentModel名前空間に属する。

サンプル・アプリケーションのソース・コード

 今回のサンプル・プログラムでは、[スタート]ボタンのClickイベント・ハンドラで、RunWorkerAsyncメソッドを呼び出して、 のDoWorkイベント・ハンドラの実行を開始する。

 また、 のProgressChangedイベント・ハンドラでは、ProgressBarコントロールのValueプロパティに進ちょく率を示す値を代入してバーの更新を行う。

 以下に、サンプル・アプリケーションのソース・コードを示す(Form1.cs/Form1.vbのみ。ソリューション全体はリストの脚注からダウンロード可)。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BGWorker1 {
  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
    }

    // [スタート]ボタンのイベント・ハンドラ
    private void buttonStart_Click(object sender, EventArgs e) {
      buttonStart.Enabled = false;

      // 時間のかかる処理を別スレッドで開始
      bgWorker.RunWorkerAsync(100);
      // DoWorkイベント発生
    }

    // 時間のかかる処理を行うメソッド
    private void bgWorker_DoWork(object sender, DoWorkEventArgs e) {
      // 別スレッドで実行されるため、このメソッドでは
      // UI(コントロール)を操作してはいけない

      // このメソッドへのパラメータ
      int bgWorkerArg = (int)e.Argument;

      // senderの値はbgWorkerの値と同じ
      BackgroundWorker worker = (BackgroundWorker)sender;

      // 時間のかかる処理
      for (int i = 0; i < bgWorkerArg; i++) {

        System.Threading.Thread.Sleep(100);

        int percentage = i * 100 / bgWorkerArg; // 進ちょく率
        worker.ReportProgress(percentage);
        // ProgressChangedイベント発生
      }
      // このメソッドからの戻り値
      e.Result = "すべて完了";

      // この後、RunWorkerCompletedイベントが発生
    }

    private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
      // 進ちょく状況の表示
      this.Text = e.ProgressPercentage + "%完了";
      progressBar.Value = e.ProgressPercentage;
    }

    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
      // 処理結果の表示
      this.Text = e.Result.ToString();
      MessageBox.Show("正常に完了");

      buttonStart.Enabled = true;
    }
  }
}
Imports System.ComponentModel

Public Class Form1

  ' [スタート]ボタンのイベント・ハンドラ
  Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles buttonStart.Click
    buttonStart.Enabled = False

    ' 時間のかかる処理を別スレッドで開始
    bgWorker.RunWorkerAsync(100)
    ' DoWorkイベント発生
  End Sub

  ' 時間のかかる処理を行うメソッド
  Private Sub bgWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
    ' 別スレッドで実行されるため、このメソッドでは
    ' UI(コントロール)を操作してはいけない

    ' このメソッドへのパラメータ
    Dim bgWorkerArg As Integer = CType(e.Argument, Integer)

    ' senderの値はbgWorkerの値と同じ
    Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)

    ' 時間のかかる処理
    For i As Integer = 1 To bgWorkerArg

      System.Threading.Thread.Sleep(100)

      Dim percentage As Integer = i * 100 / bgWorkerArg ' 進ちょく率
      worker.ReportProgress(percentage)
      ' ProgressChangedイベント発生
    Next

    ' このメソッドからの戻り値
    e.Result = "すべて完了"

    ' この後、RunWorkerCompletedイベントが発生
  End Sub

  Private Sub bgWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgWorker.ProgressChanged
    ' 進ちょく状況の表示
    Me.Text = e.ProgressPercentage & "%完了"
    progressBar.Value = e.ProgressPercentage
  End Sub

  Private Sub bgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
    ' 処理結果の表示
    Me.Text = e.Result.ToString()
    MessageBox.Show("正常に完了")

    buttonStart.Enabled = True
  End Sub
End Class
進ちょく状況を表示しながら重い処理を行うサンプル・プログラム(上:Form1.cs、下:Form1.vb)

 注意すべきは、DoWorkイベント・ハンドラだけは別スレッドで実行されるため、そのメソッド内でコントロールのプロパティやメソッドを操作してはいけないという点だ(そのような操作を行うと.NET Framework 2.0では例外が発生する)。

 また、DoWorkイベント・ハンドラ実行中に、さらにRunWorkerAsyncメソッドを呼び出すことはできない(例外が発生する)。それが実行中かどうかはIsBusyプロパティにより確認できる。今回のサンプル・プログラムでは、ボタンの有効/無効を切り替えて多重な実行を禁止している。End of Article

利用可能バージョン:.NET Framework 2.0のみ
カテゴリ:Windowsフォーム 処理対象:スレッド
使用ライブラリ:BackgroundWorkerコンポーネント(System.ComponentModel名前空間)
使用ライブラリ:DoWorkEventArgsクラス(System.ComponentModel名前空間)
使用ライブラリ:ProgressChangedEventArgsクラス(System.ComponentModel名前空間)
使用ライブラリ:RunWorkerCompletedEventArgsクラス(System.ComponentModel名前空間)
関連TIPS:バックグラウンド処理を途中でキャンセルするには?

この記事と関連性の高い別の.NET TIPS
バックグラウンド処理を途中でキャンセルするには?
WPF/Windowsフォーム:時間のかかる処理をバックグラウンドで実行するには?(async/await編)[C#/VB]
Windowsフォームで別スレッドからコントロールを操作するには?
WPF:DataGridやListViewなどに表示しているデータを別スレッドから変更するには?
システムトレイ(タスクトレイ)にアイコンを表示するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間