戻り値があり、パラメーターが無い Task (Task<T>) の利用 - C#

戻り値があり、パラメーターが無いTaskを利用するコードを紹介します。

概要

Taskオブジェクトの処理結果で値を呼び出し元に返したい場合があります。Taskで戻り値を返す場合には、Taskで処理する関数(メソッド)でreturnを用いて戻り値を返します。 メインスレッド側では、TaskオブジェクトのResult プロパティを利用して値を取得します。
また、戻り値がある場合のTaskオブジェクトは Task<戻り値の型> の形式で宣言します。

プログラム1 : メソッドを利用してTaskオブジェクトを作成する場合

Windows Form アプリケーションを作成します。

UI

下図のフォームを作成します。テキストボックスとボタンを配置します。このプログラムでは[button1]のみ利用します。

コード

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

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

    private void button1_Click(object sender, EventArgs e)
    {
      Task<string> task1 = new Task<string>(proc1);
      Task<string> task2 = new Task<string>(proc2);

      task1.Start();
      task2.Start();

      textBox1.Text = task1.Result + "," + task2.Result;
    }

    private string proc1()
    {
      Thread.Sleep(2000);
      return "ぺんぎん";
    }

    private string proc2()
    {
      Thread.Sleep(3000);
      return "あひる";
    }
  }
}

解説

Taskオブジェクトを作成します。戻り値がある場合のTaskオブジェクトは Task<戻り値の型> の型になります。
コンストラクタの引数にTaskオブジェクトで実行するメソッドを与えます。
今回はproc1を実行するTask、proc2 を実行するTaskの2つのTaskオブジェクトを作成します。
  Task<string> task1 = new Task<string>(proc1);
  Task<string> task2 = new Task<string>(proc2);

作成されたTaskオブジェクトの実行は、Start メソッドを呼び出します。
  task1.Start();
  task2.Start();

Taskオブジェクトで処理されるメソッドは以下です。重い処理を再現するため、Sleepメソッドでスレッドを停止しています。
Task処理の戻り値は、return で返します。
    private string proc1()
    {
      Thread.Sleep(2000);
      return "ぺんぎん";
    }

    private string proc2()
    {
      Thread.Sleep(3000);
      return "あひる";
    }
Taskの処理結果を受け取り、TextBoxに表示します。Taskの処理結果はTaskオブジェクトのResultプロパティから取得します。
Resultの値が返るまでTaskの終了を待ちます。Taskの終了を待つ間スレッドはブロックされるため、UIの操作ができなくなります。
  textBox1.Text = task1.Result + "," + task2.Result;

実行結果

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


[button1]をクリックします。ボタンクリックから2秒ほどして、テキストボックスに結果が表示されます。
結果待ちしている間はメインスレッドで待ち状態になるため、ウィンドウが固まることも確認できます。

プログラム2 : ラムダ式を利用してTaskオブジェクトを作成する場合

UI

下図のフォームを作成します。テキストボックスとボタンを配置します。このプログラムでは[button2]のみ利用します。

コード

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

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

    private void button2_Click(object sender, EventArgs e)
    {
      Task<string> task1 = new Task<string>(() => proc1());
      Task<string> task2 = new Task<string>(() => proc2());

      task1.Start();
      task2.Start();

      textBox1.Text = task1.Result + "," + task2.Result;
    }

    private string proc1()
    {
      Thread.Sleep(2000);
      return "ぺんぎん";
    }

    private string proc2()
    {
      Thread.Sleep(3000);
      return "あひる";
    }
  }
}

解説

先のプログラムと処理の内容は同じですが、Taskオブジェクトの作成のコンストラクタの第一引数にラムダ式を与えています。
ラムダ式についてはこちらの記事を参照して下さい。
  Task<string> task1 = new Task<string>(() => proc1());
  Task<string> task2 = new Task<string>(() => proc2());

実行結果

プロジェクトを実行し、[button2]をクリックします。先ほどのプログラムと同様、ボタンのクリックから、3秒ほどして結果がテキストボックスに表示されます。
ボタンクリックから結果が表示されるまでは、UIスレッドが停止するため、ウィンドウの操作ができなくなることも確認できます。

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

パラメーターが無く、戻り値がある場合には、Runメソッドを利用した記述ができます。

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.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

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

    private void button3_Click(object sender, EventArgs e)
    {
      Task<string> t1 = Task<string>.Run<string>(proc1);
      Task<string> t2 = Task<string>.Run<string>(proc2);

      textBox1.Text = t1.Result + "," + t2.Result;
    }

    private string proc1()
    {
      Thread.Sleep(2000);
      return "ぺんぎん";
    }

    private string proc2()
    {
      Thread.Sleep(3000);
      return "あひる";
    }
  }
}

解説

Taskオブジェクトに送るパラメーターが無い場合は、Runメソッドを利用できます。
  Task<string> t1 = Task<string>.Run<string>(proc1);
  Task<string> t2 = Task<string>.Run<string>(proc2);

実行結果

プロジェクトを実行し、[button3]をクリックします。ボタンのクリックから、3秒ほどして結果がテキストボックスに表示されます。
ボタンクリックから結果が表示されるまでは、UIスレッドが停止するため、ウィンドウの操作ができなくなることも確認できます。

補足 : ウィンドウが固まらないようにする場合

今回紹介したコードでは結果を待つ間メインスレッドがブロックされるため、メインウィンドウがロックされた状態になってしましいます。 ウィンドウが固まらないようにする方法はいくつかあり、スレッド側からコールバックする方法や非同期関数を利用する方法があります。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-08-24
作成日: 2020-05-07
iPentec all rights reserverd.