.net中ThreadPool与Task的认识总结

     线程池和Task是多线程编程中两个常用的技术,你们在熟悉不过了。他们有什么关联关系?Task又是怎么工做的呢?估计不少时候会犯糊涂。经过翻阅资料,终于弄明白了,与你们分享一下。编程

 

工做线程与I/O线程
     在ThreadPool中有这样一个方法:windows

public static bool SetMaxThreads(int workerThreads, int completionPortThreads);多线程

    此方法中有两个参数:workerThreads和completionPortThreads。这两个参数引伸出了两个概念:辅助线程(也叫工做线程)和异步 I/O 线程。这两个线程有什么区别么?经过查阅资料,咱们能够了解到,工做线程其实就是咱们编码主动向ThreadPool中建立的线程。而I/O线程是线程池中预先保留出来的部分线程,这部分线程的做用是为了分发从IOCP(I/O completion port) 中的回调。异步

    那么什么是IOCP回调呢?函数

    在CLR内部,系统维护了一个IOCP(I/O completion port),它提供了处理多个异步I/O请求的线程模型。咱们能够把IOCP看作是一个消息队列。当一个进程建立了一个IOCP,即建立了一个队列。当异步I/O请 求完成时,设备驱动程序就会生成一个I/O完成包,将它按照FIFO方式排队列入该完成端口。以后,会由I/O线程提取完成I/O请求包,并调用以前的委托。注意:异步调用服务时,回调函数都是运行于CLR线程池的I/O线程当中性能

    I/O线程是由CLR调用的,一般状况下,咱们不会直接用到它 。可是线程池中区分它们的目的是为了不线程都去处理I/O回调而被耗尽,从而引起死锁。在编程时,开发人员须要关注的是确保I/O线程返回到线程池,I/O回调代码应该作尽可能小的工做,并尽快返回到线程池,不然I/O线程会很快消耗光。若是回调代码中的工做不少的话,应该考虑把工做拆分到一个工做者线程中去。不然,I/O线程被耗尽,大量工做线程空闲,可能致使死锁。编码

    再补充一下,当执行I/O操做的时候,不管是同步I/O操做仍是异步I/O操做,都会调用的Windows的API方法,好比,当读取文件时,调用ReadFile函数。该方法会将你的当前线程从用户态转变成内核态,会生成一个I/O请求包,并初始化这个请求包。ReadFile会向内核传递,根据这个请求包,windows内核知道须要将这个I/O操做发送给哪一个硬件设备。这些I/O操做会进入设备本身的处理队列中,该队列由这个设备的驱动程序维护。spa

    若是此时是同步I/O操做,那么在硬件设备操做I/O的时候,发出I/O请求的线程因为无事可作被windows变成睡眠状态,当硬件设备完成操做后,再唤醒这个线程。这种方式很是直接,可是性能不高,若是请求数不少,那么休眠的线程数也不少,浪费了大量资源。线程

    若是是异步I/O操做(.Net中,异步的I/O操做大部分为BeginXXX的形式 ),该方法在Windows把I/O请求包发送到设备的处理队列后就返回。同时,在调用异步I/O操做时,即调用BeginXXX方法的时候,须要传入一个委托,该委托方法会随着I/O请求包一路传递到设备的驱动程序。在设备处理完I/O请求包后,将该委托再放到CLR线程池队列。设计

    总结来讲,IOCP(I/O completion port)中有2个队列,一个是先进先出的队列,存放的是IO完成包,即已经完成的IO操做须要执行回调方法。还有一个队列是线程队列,IOCP会预分配一些线程在这个队列中,这样会比即时建立线程处理I/O请求速度更快。这个队列是后进先出的,好处是下一个请求的到来可能仍是用以前的线程来处理,就不须要进行线程上下文切换,提升了性能。

    这里有一个IOCP的解释,写的很好。http://gamebabyrocksun.blog.163.com/blog/static/57153463201036104134250/ 

Task的运行原理分析

    Task与ThreadPool什么关系呢?简单来讲,Task是基于ThreadPool实现的,固然被标记为LongRunning的Task(单首创建线程实现)除外。Task被建立后,经过TaskScheduler执行工做项的分配。TaskScheduler会把工做项存储到两类队列中: 全局队列与本地队列。全局队列被设计为FIFO的队列。本地队列存储在线程中,被设计为LIFO.

    当主程序建立了一个Task后,因为建立这个Task的线程不是线程池中的线程,则TaskScheduler 会把该Task放入全局队列中。

    若是这个Task是由线程池中的线程建立,而且未设置TaskCreationOptions.PreferFairness标记(默认状况下未设置),TaskScheduler 会把该Task放入到该线程的本地队列中。若是设置了TaskCreationOptions.PreferFairness标记,则放入全局队列。

    官方的解释是: 最高级任务(即不在其余任务的上下文中建立的任务)与任何其余工做项同样放在全局队列上。 可是,嵌套任务或子任务(在其余任务的上下文中建立)的处理方式大不相同。 子任务或嵌套任务放置在特定于执行父任务的线程的本地队列上。 父任务多是最高级任务,也多是其余任务的子任务

    那么任务放入到两类队列中后,是如何被执行的呢?

    当线程池中的线程准备好执行更多工做时,首先查看本地队列。 若是工做项在此处等待,直接经过LIFO的模式获取执行。 若是没有,则向全局队列以FIFO的模式获取工做项。若是全局队列也没有工做项,则查看其余线程的本地队列是否有可执行工做项,若是存在可执行工做项,则以FIFO的模式出队执行

相关文章
相关标签/搜索