【摘要】
Task标识一个异步操作,是.NET 4.0中新引入的基于任务的编程模型。此模型可以发挥多核的功效,提升应用程序的性能。
一、多任务串行执行
我们先来看一下多任务线性执行时程序需要花费的时间。假设我们有A、B、C三个任务需要执行,完成任务A需要4秒,完成任务B需要3秒,完成任务C需要5秒。串行执行的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTest
{
class Program
{
static void Main(string[] args)
{
DateTime startTime = DateTime.Now;
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} Start.", startTime));
SyncTask();
DateTime endTime = DateTime.Now;
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} End.", endTime));
TimeSpan costTime = endTime - startTime;
Console.WriteLine(string.Format("TotalSeconds:{0}", costTime.TotalSeconds));
Console.ReadKey();
}
static void SyncTask()
{
TaskA();
TaskB();
TaskC();
}
static void TaskA()
{
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskA Start.", DateTime.Now));
Thread.Sleep(4000);
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskA Finish.", DateTime.Now));
}
static void TaskB()
{
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskB Start.", DateTime.Now));
Thread.Sleep(3000);
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskB Finish.", DateTime.Now));
}
static void TaskC()
{
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskC Start.", DateTime.Now));
Thread.Sleep(5000);
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} TaskC Finish.", DateTime.Now));
}
}
}
运行结果如下图:
当按顺序执行任务ABC时,程序总花费时间约为12秒(4+3+5)。
二、多任务并行执行
接下来使用Task同时启动任务ABC,看看程序的总花费时间。
添加并行执行任务的方法:
static void AsyncTask()
{
Task taskA = new Task(() => { TaskA(); });
Task taskB = new Task(() => { TaskB(); });
Task taskC = new Task(() => { TaskC(); });
taskA.Start();
taskB.Start();
taskC.Start();
List<Task> taskList = new List<Task>();
taskList.Add(taskA);
taskList.Add(taskB);
taskList.Add(taskC);
Task.WaitAll(taskList.ToArray());
}
构建Task时需要将要执行的任务封装成Action传递给Task的构造函数,然后通过Task.Start()方法启动任务,Task.WaitAll表示等待列表中所有任务执行完再进行下一步操作。
在Main方法中调用AsyncTask:
static void Main(string[] args)
{
DateTime startTime = DateTime.Now;
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} Start.", startTime));
AsyncTask();
DateTime endTime = DateTime.Now;
Console.WriteLine(string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} End.", endTime));
TimeSpan costTime = endTime - startTime;
Console.WriteLine(string.Format("TotalSeconds:{0}", costTime.TotalSeconds));
Console.ReadKey();
}
运行结果如下:
可以看到三个任务并行运行时,程序的总花费时间只有5秒,与三个任务中单个任务的最长花费时间差不多。
三、 多任务的其他几种执行方式
上一节中任务B与任务A是并行执行的,如果任务B需要等待任务A执行完才能开始执行,任务B与任务A就构成了一个任务链,相应代码调整如下:
static void AsyncTask()
{
Task taskA = new Task(() => { TaskA(); });
var taskAB = taskA.ContinueWith((t) => { TaskB(); });
Task taskC = new Task(() => { TaskC(); });
taskA.Start();
taskC.Start();
List<Task> taskList = new List<Task>();
taskList.Add(taskA);
taskList.Add(taskAB);
taskList.Add(taskC);
Task.WaitAll(taskList.ToArray());
}
通过Task. ContinueWith()方法实现了任务B在任务A完成后开始执行,同时只需要启动任务A即可,不需要再单独启动任务B。注意到Task. ContinueWith()方法的返回值也是Task实例,这就意味着可以通过持续调用Task. ContinueWith()方法构造包含任意个任务的任务链。
代码运行效果如下图:
如果任务AB是并行执行,而任务C需要等待任务AB都完成后才开始执行,那么代码需要做如下调整:
static void AsyncTask()
{
Task taskA = new Task(() => { TaskA(); });
Task taskB = new Task(() => { TaskB(); });
taskA.Start();
taskB.Start();
List<Task> taskList = new List<Task>();
taskList.Add(taskA);
taskList.Add(taskB);
Task taskC = Task.Factory.ContinueWhenAll(taskList.ToArray(), (t) => { TaskC(); });
taskC.Wait();
}
通过Task.Factory.ContinueWhenAll()方法,实现了任务C等待任务AB都完成后再启动的效果,Task.Factory.ContinueWhenAll()的返回值也是Task实例,因此可以通过Task.Wait()方法等待任务C的完成再进行下一步操作。
代码运行结果如下图:
本文介绍了Task在多任务场景中的几种常见使用方式,Task还提供了非常多灵活多变的使用方式和使用技巧,可以满足各种复杂场景下的多任务运行,有兴趣的读者可以利用MSDN相关文档以及其他资料进行深入研究。转自http://www.learnfuture.com/article/1704