线程池和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. go基础编程 day-2

    Go的常亮 关键字: Const Go常亮的多个定义: // 定义常亮的关键字 const // 定义多个常亮 const( PI = 3.14 const1 = " const2 = co ...

  2. LeetCode 216. Combination Sum III (组合的和之三)

    Find all possible combinations of k numbers that add up to a number n, given that only numbers from ...

  3. FastDFS与Nginx的配置说明

    1.简介     FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合以文件为载 ...

  4. 实验排队功能实现(JAVA)

    1.功能要求 实验室有固定台数的设备供学生通过网络连接进行实验,一台设备只能同时被一个用户使用,一个用户只能占用一台设备. 下面是一个功能的简图: 2.实现方案 2.1 初始化 在项目启动之后,开始进 ...

  5. DataTable数据修改,换列

    增加列             DataTable table= new DataTable();             table.Columns.Add("ID", type ...

  6. leaflet+esri-leaflet+heatmap实现热力图

    1.环境准备 1)下载leaflet.js插件,  官网传送:http://leafletjs.com/ 2)下载esri-leaflet.js插件,  官网传送:http://esri.github ...

  7. 基于netfilter和LVM的密码窃取

    一:要求: 编写一个基于netfilter的模块,该模块的功能是捕获如mail.ustc.edu.cn等使用明文传输用户名和密码的网站的用户名和密码:并在接收到特定的ICMP数据包之后将捕获的用户名和 ...

  8. Linux学习(十六)VIM

    一.简介 VIM是vi的增强版.VIM是Linux平台上的主要编辑器.基本上所有的文档的新增,修改,保存都需要用到它.所以,掌握VIM是很有必要的. vim的安装非常简单,一条命令就可以了: yum ...

  9. 使用js编写一个简单的运动框架

    下班后,,没事捣鼓捣鼓个人的小爱好. 首先,说明我的这个运动框架(css所有属性)也是常见的框架一种,健壮性并不是太好,对于新手学习倒是挺好,,若是大神,老司机请拐弯. 上来,我们先定义一个区块,然后 ...

  10. Postman参数化使用以及中文乱码问题解决

    1.参数化详解 准备工作,数据准备 2.使用csv文件时中文乱码可以通过使用txt文本,json文本改变调用json文件改变文件的编码格式解决 3:参数化数据调用的两种方式通过调用读取文件传入环境变量 ...