C#のthreadとtaskのTask
概要
TaskはNET Framework 4に追加されました.これは新しいnamespace:
System.Threading.Tasks
です.adding parallelism and concurrency to applications
を強調しています.現在はマルチコアCPUであり、システム内ではTask Parallel LibraryがCPUコアをより効率的に利用できる.TPLは、利用可能なすべてのプロセッサを最も効率的に使用するために、同時性の度合いを動的に比例して調整します.TPLはまた、ワークパーティション、ThreadPool上のスレッドスケジューリング、サポート解除、ステータス管理、およびその他の低レベルの詳細な操作を処理します.ThreadPoolのThreadと比較して、Cancelの操作はあまりサポートされていません.
文法的にはlambda式とよりよく結合されています.
Taskの作成
Taskを明示的に作成
簡単な例を見てみましょう.
using System;
using System.Threading;
using System.Threading.Tasks;
public class TaskExample1
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// lambda , delegate, Task
Task taskA = new Task( () => Console.WriteLine("From taskA."));
// Start
taskA.Start();
// Output a message from the calling thread.
Console.WriteLine("From thread '{0}'.",
Thread.CurrentThread.Name);
// task
taskA.Wait();
}
}
// From thread 'Main'.
// From taskA.
タスクオブジェクトTaskは、タスクの生存期間全体にわたって呼び出しスレッドからアクセスできるメソッドと属性を提供します.たとえば、タスクのStatusプロパティにいつでもアクセスして、実行が開始されたか、実行が完了したか、キャンセルされたか、例外が発生したかを判断できます.ステータスはTaskStatusの列挙で表されます.
作成して開始したTask操作バージョンのシンプル化–1つの操作を直接使用:
System.Threading.Tasks.Task.Run()
using System;
using System.Threading;
using System.Threading.Tasks;
public class TaskExample2
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// Define and run the task.
Task taskA = Task.Run( () => Console.WriteLine("From taskA."));
// Output a message from the calling thread.
Console.WriteLine("From thread '{0}'.",
Thread.CurrentThread.Name);
taskA.Wait();
}
}
// From thread 'Main'.
// From taskA.
TaskFactory Taskの作成
TPLはファクトリクラスTaskFactoryを提供し、直接Taskを作成することもできます.1つの操作
System.Threading.Tasks.TaskFactory.StartNew
は、Taskの作成および開始を完了する.Use this method when creation and scheduling do not have to be separated and you require additional task creaion options or the use of a specific scheduler, or when you need to pass additional state into the task through its AsyncState property, as shown in the following example.
using System;
using System.Threading;
using System.Threading.Tasks;
public class TaskExample3
{
public static void Main()
{
Thread.CurrentThread.Name = "Main";
// task
Task taskA = Task.Factory.StartNew(() => Console.WriteLine("From taskA."));
//
Console.WriteLine("From thread '{0}'.",
Thread.CurrentThread.Name);
// wait task
taskA.Wait();
}
}
// Hello from thread 'Main'.
// Hello from taskA.
入力パラメータ
MSからの例:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
Task[] taskArray = new Task[10];
for (int i = 0; i < taskArray.Length; i++) {
taskArray[i] = Task.Factory.StartNew( (int param) => {
Console.WriteLine("Task #{0}.", param);
},
i );
}
Task.WaitAll(taskArray);
}
}
Task戻り値
Task実行メソッドに戻り値がある場合は、その値を得ることができます.taskを作成すると、その戻り値はTaskであり、戻りタイプTのTaskを表す.taksの実行が完了すると、TaskのResultプロパティで結果を取得できます.
public class Task : System.Threading.Tasks.Task
MSからの例:
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main()
{
// lambda task , 。
Task<int> task1 = Task<int>.Factory.StartNew(() => 1);
int i = task1.Result;
//
Task task2 = Task.Factory.StartNew(() =>
{
string s = ".NET";
double d = 4.0;
return new Test { Name = s, Number = d };
});
Test test = task2.Result;
// Return an array produced by a PLINQ query
Task<string[]> task3 = Task<string[]>.Factory.StartNew(() =>
{
string path = @"C:\Users\Public\Pictures\Sample Pictures\";
string[] files = System.IO.Directory.GetFiles(path);
var result = (from file in files.AsParallel()
let info = new System.IO.FileInfo(file)
where info.Extension == ".jpg"
select file).ToArray();
return result;
});
foreach (var name in task3.Result)
Console.WriteLine(name);
}
class Test
{
public string Name { get; set; }
public double Number { get; set; }
}
}
Continue操作
Taskの実行開始時間が他のTaskの完了に依存する場合、Continueシリーズメソッドを使用します.
MSからの例:
using System;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
var getData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} );
var processData = getData.ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} );
var displayData = processData.ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
// :
// N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
また、より簡略化する書き方を用いることができる.
using System;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
var displayData = Task.Factory.StartNew(() => {
Random rnd = new Random();
int[] values = new int[100];
for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
values[ctr] = rnd.Next();
return values;
} ).
ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
sum += x.Result[ctr];
mean = sum / (double) n;
return Tuple.Create(n, sum, mean);
} ).
ContinueWith((x) => {
return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
x.Result.Item1, x.Result.Item2,
x.Result.Item3);
} );
Console.WriteLine(displayData.Result);
}
}
Cancel操作
TPLでは、Taskがcancelで実行できるように、CancellationTokenSourceとCancellationTokenが提供されています.完了する必要がある作業は、Taskのaction実行メソッド内で、CancelationTokenのIsCancelationRequestedプロパティを周期的にチェックすることです.例:
private var tokenSource = new CancellationTokenSource();
public void Start()
{
var token = tokenSource.Token;
for (int i = 0; i<5; i++>)
{
Task t = Task.Factory.StartNew( () => DoSomeWork(i, token), token);
Console.WriteLine("Task {0} executing", t.Id);
}
}
public void Stop()
{
var token = tokenSource.Token;
tokenSource.Cancel();
}
void DoSomeWork(int taskNum, CancellationToken ct)
{
// , , cancel 。
if (ct.IsCancellationRequested == true) {
Console.WriteLine("Task {0} was cancelled before it got started.",
taskNum);
ct.ThrowIfCancellationRequested(); // -- return
}
// 。
int maxIterations = 100;
// NOTE!!! A "TaskCanceledException was unhandled
for (int i = 0; i <= maxIterations; i++) {
//
var sw = new SpinWait();
for (int j = 0; j <= 100; j++)
sw.SpinOnce();
if (ct.IsCancellationRequested) {
Console.WriteLine("Task {0} cancelled", taskNum);
ct.ThrowIfCancellationRequested(); // -- return
}
}
}