建议80:用Task代替ThreadPool
ThreadPool相对于Thread来说具有很多优势,但是ThreadPool在使用上却存在一定的不方便。比如:
ThreadPool不支持线程的取消、完成、失败通知等交互性操作。
ThreadPool不支持线程执行的先后次序。
以往,如果开发者要实现上述功能,需要完成很多额外的工作。现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL 4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式了。
以下是一个简单的任务示例:
static void Main(string[] args) { Task t = new Task(() => { Console.WriteLine("任务开始工作……"); //模拟工作过程 Thread.Sleep(5000); }); t.Start(); t.ContinueWith((task) => { Console.WriteLine("任务完成,完成时候的状态为:"); Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); }); Console.ReadKey(); } |
任务Task具备以下属性,可以让我们查询任务完成时的状态:
IsCanceled 因为被取消而完成 IsCompleted 成功完成 IsFaulted 因为发生异常而完成 |
需要注意的是,任务并没有提供回调事件来通知完成(像BackgroundWorker一样),它是通过启用一个新任务的方式来完成类似的功能。ContinueWith方法可以在一个任务完成的时候发起一个新任务,这种方式天然就支持了任务的完成通知:我们可以在新任务中获取原任务的结果值。
下面是一个稍微复杂的例子,同时支持完成通知、取消、获取任务返回值等功能:
static void Main(string[] args) { CancellationTokenSource cts = new CancellationTokenSource(); Task<int> t = new Task<int>(() => Add(cts.Token), cts.Token); t.Start(); t.ContinueWith(TaskEnded); //等待按任意键取消任务 Console.ReadKey(); cts.Cancel(); Console.ReadKey(); } static void TaskEnded(Task<int> task) { Console.WriteLine("任务完成,完成时候的状态为:"); Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted); Console.WriteLine("任务的返回值为:{0}", task.Result); } static int Add(CancellationToken ct) { Console.WriteLine("任务开始……"); int result = 0; while (!ct.IsCancellationRequested) { result++; Thread.Sleep(1000); } return result; } |
在任务开始后大概3秒的时候按下键盘,会得到如下的输出:
任务完成,完成时候的状态为:
IsCanceled=False IsCompleted=True IsFaulted=False 任务的返回值为:3 |