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

 

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

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操作会进入设备自己的处理队列中,该队列由这个设备的驱动程序维护。

如果此时是同步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请求速度更快。这个队列是后进先出的,好处是下一个请求的到来可能还是用之前的线程来处理,就不需要进行线程上下文切换,提高了性能。

 

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的模式出队执行。

.net中ThreadPool与Task的认识总结的更多相关文章

  1. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  2. .NET 异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  3. 线程池ThreadPool及Task调度死锁分析

    近1年,偶尔发生应用系统启动时某些操作超时的问题,特别在使用4核心Surface以后.笔记本和台式机比较少遇到,服务器则基本上没有遇到过. 这些年,我写的应用都有一个习惯,就是启动时异步做很多准备工作 ...

  4. NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL

    .NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL,异常处理,线程取消 今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主 ...

  5. .NET异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  6. 线程阶段性总结——APM,ThreadPool,Task,TaskScheduler ,CancellationTokenSource

    不管我们使用thread,threadPool,task,还是APM异步,本质都是在使用多线程.对于新手来说,不太敢用多线程的原因,就我个人的体验来说,就是对多线程的异常捕获方式或时机缺乏了解,而一旦 ...

  7. Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端

    多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞.线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程 ...

  8. java中ThreadPool的介绍和使用

    文章目录 Thread Pool简介 Executors, Executor 和 ExecutorService ThreadPoolExecutor ScheduledThreadPoolExecu ...

  9. C#异步和多线程以及Thread、ThreadPool、Task区别和使用方法

    本文的目的是为了让大家了解什么是异步?什么是多线程?如何实现多线程?对于当前C#当中三种实现多线程的方法如何实现和使用?什么情景下选用哪一技术更好? 第一部分主要介绍在C#中异步(async/awai ...

随机推荐

  1. 使用acs-engine在Azure中国区部署kubernetes集群详解

    转载请注明出处:http://www.cnblogs.com/wayneiscoming/p/7649642.html 1. acs-engine简介 ACS是微软在2015年12月推出的一项基于容器 ...

  2. Uva11582

    最近各种破事忙死了 终于开始做题了 紫薯第10章第一题,come on 设g(i)=f(i) mod n,当二元组(g(i).g(i+1))出现重复时,整个序列就开始重复(这一话怎么也不懂,请大神解释 ...

  3. Java中对图片文件的类型的获取

    public static void main(String[] args) {        File f = new File("c://test.jpg");         ...

  4. NOIP2012疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  5. Spark 基本概念

    Application:用户编写的 Spark 应用程序,包含驱动程序(Driver),和分布在集群中多个节点上运行的 Executor 代码,在执行过程中由一个或多个作业组成 Driver(驱动程序 ...

  6. 微信小程序之页面下拉刷新

    如果需要给单个页面设置下拉刷新功能,不需要写在""window"对象里面,直接在  文件名称.json 里面设置即可 { "enablePullDownRefr ...

  7. angular-utils-ui-breadcrumbs使用心得

    angular-utils-ui-breadcrumbs是一个用来自动生成面包屑导航栏的一个插件,需要依赖angular.UIRouter和bootstrap3.css.生成的界面截图如下,点击相应的 ...

  8. @RequestBody对象为空,异常Required request body is missing

    1.异常 org.springframework.http.converter.HttpMessageNotReadableException: Required request body is mi ...

  9. 【Win 10 应用开发】UI Composition 札记(二):基本构件

    在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”. UI Composition 有一个核心类——对,就是 C ...

  10. Cycling

    Cycling Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...