このページの最終更新日 2020/10/25

Taskを使用して中断処理を実装する

1. Taskを使ってみる

 このページでは「Task」を使って中断処理を実装していくことにします。Taskのキャンセルを行うのは「CancellationToken 」を使用するべきかとは思いますが、ここではとりあえず順々に理解できるものとして簡易的にコードを記述しています。先ほどと同じように クラス変数として _isCancelled 変数を用意して、「中止」ボタンを押下した場合は _isCancelled が true に変更され、タスクの中で _isCancelled が true になっていると判定された場合は例外を投げて処理が中断されるようにします。

- code.1 -
public partial class FormMain : Form //Taskを使ったサンプル
{
    private Task _task;
    private bool _isCancelled;

   public FormMain()
   {
       InitializeComponent();
       this.btnExe.Click += new EventHandler(BtnExeClick);
        this.btnCancel.Click += new EventHandler(BtnCancelClick);
    }

    private void BtnExeClick(object sender, EventArgs e)
    {
        _task = new Task(ExecuteTask);
        _task.Start();
    }

    private void  ExecuteTask()
    {
        try
        {
            for (int iProg = 1; iProg <= 100; iProg++)
            {
                Task.Delay(100).Wait();
                if (_isCancelled) { throw new Exception("ユーザー操作により中止されました。"); }
                this.Invoke((Action)(() =>{
                    this.pbWork.Value = iProg;
                }));
            } 
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void BtnCancelClick(object sender, EventArgs e)
    {
        _isCancelled = true;
    }
}

実際に「中止」ボタンをクリックしたときに表示される画面です。26行目→34行目のコードが実行されメッセージボックスが表示されます。

プログレスバー更新を中断したときに表示されるエラーメッセージ

- pic.1 -

また、27行目-29行目のthis.Invoke~ の箇所

- code.2 -
    this.Invoke((Action)(() =>{
    	this.pbWork.Value = iProg;
    }));

ここを、単に「this.pbWork.Value = iProg;」と28行のみの一文で書いてしまうとpic.3のようなエラーメッセージが表示されます。

「有効ではないスレッド間の操作」というエラーメッセージ

- pic.2 -

何でエラーが発生するのかというのは、MicroSoftのページの情報を抜粋してそのまま記載しておくことにします。
「マルチスレッドを使用して Windows フォーム アプリケーションのパフォーマンスを向上させる場合は、必ずスレッド セーフな方法でコントロールを呼び出してください。(- 途中略 -) Invoke メソッドを使用せずにコントロールを作成したスレッド以外のスレッドからコントロールを呼び出すのは安全ではありません。」