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
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操作版本–直接使用一个操作: 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。 一个操作System.Threading.Tasks.TaskFactory.StartNew
, 即可完成创建并开始一个Task.
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.
传入参数
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属性获取结果。
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<Test> task2 = Task<Test>.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系列方法。
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;
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);
}
}
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);
}
}
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);
}
}
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);
}
}
而且,可以使用更简化的写法.
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执行方法内,周期性地检查CancellationToken的IsCancellationRequested属性。
示例:
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
}
}
}