【导读】网上关于Task的文章如数家珍,不过有一部分并未谈到一个根本的问题,所创建的Task一定在线程池上运行?如何合理的使用Task?
这里并不会去重新讲解每一个APi的使用,没有任何意义,这属于包括我在内的各位童鞋们都必须要了解的基础前提。这里我对其作出基本总结以及使用Task时需要注意的地方
Thread与Task区别
任务可以返回结果,没有直接的机制可以从线程返回结果。
任务通过使用取消令牌来支持取消,但是线程没有。
一个任务可以同时执行多个线程,线程一次只能运行一个任务。
可以使用async和await关键字轻松实现异步。
新的Thread不处理线程池线程,而Task确实使用线程池线程。
任务是比线程更高层次的概念。
Task
.NET框架提供Threading.Tasks类,我们可以创建任务并异步运行它们,任务是代表应该完成的某些工作的对象。该任务可以告诉我们工作是否完成,并且操作返回结果,那么任务将为我们提供结果。
深入思考Task
Thread是基于操作系统级别的线程,而ThreadPool和Task不会创建自己的操作系统线程,二者是由任务调度器(TaskScheduler)执行,默认的调度程序仅仅在ThreadPool上运行,与ThreadPool不同,Task可以在指定时间返回完成结果,并且还可以通过ContinueWith延续任务,以使得任务执行完毕后运行更多操作,如果已完成立即进行回调,也可以调用Wait来同步等待任务完成,如同Thread.Join一样阻塞线程执行,直到任务完成
重点来了
由于任务在ThreadPool上运行,因此创建不加任何选项的默认Task并不适用于执行长时间的操作,因为它们可能会将系统默认的线程池给填满导致其他操作的线程阻塞,但是Task提供了LongRunning选项,设置此项后,告诉任务调度器(TaskScheduler)启动新的线程,而不是在ThreadPool上运行
var task = Task.Factory.StartNew(() => ("Hello World"), TaskCreationOptions.LongRunning);Console.WriteLine(task.Result);
任务和线程之间的最主要区别之一是异常的传播。在使用线程时,如果我们在长时间运行的方法中获得异常,则不可能在父函数中捕获该异常,但是如果我们在使用任务,则很容易捕获该异常。