Task の利用 (パラメーター、戻り値ともに無い場合) - C#

C#でパラメーターと戻り値のどちらも無い場合のTaskの記述方法を紹介します。

概要

Taskの実行方法には、Taskオブジェクトを作成し、Startメソッドを呼び出してタスクを実行開始する方法と、Runメソッドを呼び出してTaskオブジェクト作成と同時に実行する方法があります。この記事では2つのTaskの記述方法を紹介します。
補足
パラメータがあり、戻り値が無いTaskの利用についてはこちらの記事を参照して下さい。
補足
await async を利用した非同期関数でのTaskの利用についてはこちらの記事を参照して下さい。

プログラム1 : Startメソッドを利用する方法

UI

Windows Formアプリケーションを作成します。フォームにボタンを配置します。4つボタンが配置されていますが、button1のみ利用します。

コード

下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleTask
{
  public partial class FormTaskActionSimple : Form
  {
    public FormTaskActionSimple()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Task t1 = new Task(Proc1);
      t1.Start();
      Task t2 = new Task(Proc2);
      t2.Start();
    }

    private void Proc1()
    {
      Thread.Sleep(3000);//重い処理

      MessageBox.Show("Proc1が完了しました。");
    }

    private void Proc2()
    {
      Thread.Sleep(4000);//重い処理

      MessageBox.Show("Proc2が完了しました。");
    }
  }
}

解説

下記のコードでTaskオブジェクトを作成します。コンストラクタの引数にタスクで並列処理させるメソッドを与えます。
  Task t1 = new Task(Proc1);

作成したTaskオブジェクトのStartメソッドを呼び出すとタスクの処理が始まります。
  t1.Start();

並列処理されるメソッドです。最初にSleepメソッドが呼び出され、3000ミリ秒、スレッドを停止し処理を停止します。実際の利用ではこの部分に時間のかかる重い処理が実装されます。 その後「Proc1が完了しました」のメッセージボックスを表示します。
  private void Proc1()
  {
    Thread.Sleep(3000);//重い処理

    MessageBox.Show("Proc1が完了しました。");
  }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。[button1]をクリックします。


3秒ほど経過すると、[Proc1が完了しました。]のダイアログボックスが表示されます。


さらに1秒ほど経過すると、[Proc2が完了しました。]のダイアログボックスが表示されます。Proc1の完了からProc2の完了までは1秒程度しかないことから、並列してProc1とProc2が実行されていることが確認できます。


ボタンをクリックしてからダイアログボックスが表示されるまでの間、ウィンドウをドラッグして移動させることができることを確認します。メインスレッドがブロックされていないため、UIが固まらないことが確認できます。

プログラム2 : ラムダ式で記述する方法

上記のコードですが、処理するメソッドを与える部分をラムダ式で記述することもできます。

UI

下図のフォームを作成します。button3 のみを利用します。

コード

下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleTask
{
  public partial class FormTaskActionSimple : Form
  {
    public FormTaskActionSimple()
    {
      InitializeComponent();
    }

    private void button3_Click(object sender, EventArgs e)
    {
      Task t1 = new Task(() = >Proc1());
      Task t2 = new Task(() => Proc2());
      t1.Start();
      t2.Start();
    }

    private void Proc1()
    {
      Thread.Sleep(3000);//重い処理

      MessageBox.Show("Proc1が完了しました。");
    }

    private void Proc2()
    {
      Thread.Sleep(4000);//重い処理

      MessageBox.Show("Proc2が完了しました。");
    }
  }
}

解説

先のプログラムと同様のコードですが、Taskオブジェクトのコンストラクタの第一に引数にメソッドではなく、ラムダ式を与えています。
    private void button3_Click(object sender, EventArgs e)
    {
      Task t1 = new Task(() = >Proc1());
      Task t2 = new Task(() => Proc2());
      t1.Start();
      t2.Start();
    }

実行結果

button3をクリックすると、先のプログラムと同様に、3秒ほどすると、メッセージダイアログが2つ表示されます。
メッセージダイアログボックスが表示されるまでの間、メインスレッドはブロックされないため、ウィンドウの操作が固まらないことも確認できます。

プログラム3 : Runメソッドを利用する方法

UI

Windows Formアプリケーションを作成します。フォームにボタンを配置します。4つボタンが配置されていますが、button2のみ利用します。

コード

下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleTask
{
  public partial class FormTaskActionSimple : Form
  {
    public FormTaskActionSimple()
    {
      InitializeComponent();
    }

    private void button2_Click(object sender, EventArgs e)
    {      
      Task.Run(Exec1);
      Task.Run(Exec2);
      
      //この記述でもOK
      /*
      Task.Run((Action)Proc1);
      Task.Run((Action)Proc2);
      */
    }

    private void button4_Click(object sender, EventArgs e)
    {
      Task.Run(() => Proc1());
      Task.Run(() => Proc2());
    }

    private void Proc1()
    {
      Thread.Sleep(3000);//重い処理

      MessageBox.Show("Proc1が完了しました。");
    }

    private void Proc2()
    {
      Thread.Sleep(4000);//重い処理

      MessageBox.Show("Proc2が完了しました。");
    }
  }
}

解説

下記のコードでTaskオブジェクトを作成し、実行します。Runメソッドの引数にタスクで並列処理させるメソッドを与えます。
  Task.Run(Exec1);

下記のAction型でキャストする記述も可能です。(動作は同じです)
  Task.Run((Action)Proc1);

button4のコードのラムダ式を与える記述も可能です。
  Task.Run(() => Proc1());

また、RunメソッドはTaskオブジェクトを返すため、下記の記述も問題ありません。
Runメソッドを呼び出した場合はすぐにTaskが開始されるので、Startメソッドを呼び出す必要はないため、Taskオブジェクトの取得は不要ですが、結果を取得する場合や、タスクの完了を待つ場合はTaskオブジェクトの参照が必要になります。
  Task t1 = Task.Run(Proc1);
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2020-05-10
作成日: 2020-02-07
iPentec all rights reserverd.