Division By Zero

ゼロで割る

WPFアプリでバックグラウンドスレッドを使う

カスタムDOSコマンドを起動するWPFアプリを作ってみたが、そのコマンドで利用する実デバイスをつなげてテストしてみたところ、思ったより処理に時間がかかることがわかり、別スレッドで起動するようにした際のメモ。プログレスバーとキャンセル処理も加えてある。

バックグラウンドスレッドの起動は以下のように行った。

  1. BackgroundWorkerインスタンスを生成して、プログレスバーの更新とキャンセル処理に必要な設定を行う
  2. DoWorkにバックグラウンドでやりたい処理を記述。ここのみバッググラウンド処理となる
  3. ProgressChangedに進捗情報更新時にやりたい処理を記述
  4. RunWorkerCompletedにやりたい処理を記述
  5. RunWorkerAsync()でバックグラウンドスレッドを起動
    bw = new BackgroundWorker();

    bw.WorkerReportsProgress = true;
    bw.WorkerSupportsCancellation = true;

    bw.DoWork += (s, evt) =>
    {
        job(evt);
    };

    bw.ProgressChanged += (s, evt) =>
    {
        プログレスバー.Value = evt.ProgressPercentage;
    };

    bw.RunWorkerCompleted += (s, evt) =>
    {
        if (e.Cancelled) {
            job_cancelld(evt);
        }
        job_completed(evt);
    };

    bw.RunWorkerAsync();

バックグラウンドスレッドをキャンセル、つまり、メインスレッド側から途中で停止させる際の流れは以下のようになる。

  1. メインスレッド側でCancelAsync()を呼び出すと、CancellationPendingがtrueになる
  2. バックグラウンドスレッド側で、CancellationPendingを定期的に確認するようにする
  3. CancellationPendingがtrueならCancelをtrueに変更してバックグラウンドスレッドからreturnする
  4. 上二つは自分で記述する
  5. キャンセル時もRunWorkerCompletedに記述した処理が開始するので、Canceledをチェックすれば、キャンセルされたかどうかがわかる
  6. 必要ならば、キャンセル時の、通常時と異なる処理を記述

また、プログレスバーの更新などは、適宜ReportProgress()をバックグラウンドスレッドから呼び出すことになる。これらを踏まえると、バックグラウンドスレッドから呼び出す処理は下記のようになる。

private void job(DoWorkEventArgs evt)
{
    for (int i = 0; i <= 100; i += 10) {
        if (bw.CancellationPending) {
            e.Cancel = true;
           return;
         }
        10_percent_job();
        bw.ReportProgress(i);
    }
}

たとえば、キャンセルボタン等のイベントハンドラからCancelAsync()を呼べば、バックグラウンドスレッドをキャンセルできるはず。

今日はここで終了!