#背景
Taskクラスには前から興味があったのですが、引数をどう渡せば良いのか分からなかったので、いろいろ調べて自分なりにサンプルを作成してみました。
参考になれば幸いです。
#サンプルについて
時間のかかる処理として素数の集合を求めるメソッドを用意し、それを同期で呼び出す方法と非同期(Task)で呼び出す方法を試してみました。そのメソッドには素数を求める為の最大の数、例えば100を指定したら0~100までの間にある素数の配列を返すようになっています。
#苦労した点
頭が悪いので、
return new Task<List<int>>(o => GetPrimes((int)o), n);
の一文を導き出すのに1週間掛かりました(笑)。
#第2版
laughterさん、chocolamintさん、ozwkさんから改善点や解説を頂きました。
本当に勉強になりました。本当にありがとうございました。
教えて頂いた事を踏まえて、備忘録としてサンプルを修正しました。
#サンプル (第2版)
GitHub TestPrime
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace TestPrime
{
class Program
{
static void Main(string[] args)
{
const int PrimesCount = 1000000;
List<int> primes;
Prime prime = new Prime();
Stopwatch sw = new Stopwatch();
// 同期で呼び出す
sw.Reset();
sw.Start();
primes = prime.GetPrimes(PrimesCount);
sw.Stop();
Console.WriteLine("同期で呼び出す");
Console.WriteLine($"{nameof(sw.Elapsed)} = {sw.Elapsed}");
Console.WriteLine($"{nameof(primes.Count)} = {primes.Count}");
#if DEBUG
Debug.WriteLine(string.Join(" ", primes.GetRange(0, 20)));
#endif
// 同期で呼び出す
sw.Reset();
sw.Start();
// 非同期はこの呼び方でもよいが....
//Task<List<int>> task = prime.GetPrimesAsync(PrimesCount);
//task.Start();
// 利用する側(Programクラス)が非同期で呼び出す方が望ましい
Task<List<int>> task = Task.Run(() => prime.GetPrimes(PrimesCount));
sw.Stop();
// タスクが終了するまで待つ。が...
// task.ResultがWaitを兼ねている。
// つまり、Resultを取得できるようになるまで待機する。
//task.Wait();
primes = task.Result;
Console.WriteLine("\n非同期で呼び出す");
Console.WriteLine($"{nameof(sw.Elapsed)} = {sw.Elapsed}");
Console.WriteLine($"{nameof(primes.Count)} = {primes.Count}");
Console.WriteLine("\n\nPush any key!");
Console.ReadKey();
}
}
}
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TestPrime
{
/// <summary>
/// 素数を求めるクラス
/// </summary>
public class Prime
{
List<int> primes = new List<int>() { 2 };
/// <summary>
/// 引数 n までの素数を求める(同期型)
/// </summary>
/// <param name="n">求める素数の最大数</param>
/// <returns>GetPrimesを実行するTask</returns>
public Task<List<int>> GetPrimesAsync(int n)
{
// 引数を渡して復帰値を得るTaskを生成
//return new Task<List<int>>(o => GetPrimes((int)o), n);
// 上記よりシンプルな記述
// 引数はnをキャプチャし使用している
return new Task<List<int>>(() => GetPrimes(n));
}
/// <summary>
/// 引数 n までの素数を求める(非同期型)
/// </summary>
/// <param name="n">求める素数の最大数</param>
/// <returns>素数の配列</returns>
public List<int> GetPrimes(int n)
{
for (int i = 2; i <= n; i++)
{
int primesCount = primes.Count;
bool notPrimeFlag = false;
for (int j = 0; j < primesCount; j++)
{
if ((i % primes[j]) == 0)
{
notPrimeFlag = true;
j = primesCount;
break;
}
}
if (!notPrimeFlag)
{
primes.Add(i);
}
}
return primes;
}
}
}
#実行結果
コンソール
同期で呼び出す
Elapsed = 00:00:22.1918873
Count = 78498
非同期で呼び出す
Elapsed = 00:00:00.0004974
Count = 78498
Push any key!
[出力]ウィンドウ
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71