C# 多线程学习系列三之CLR线程池系列之ThreadPool
一、CLR线程池
1、进程和CLR的关系
一个进程可以只包含一个CLR,也可以包含多个CLR
2、CLR和AppDomain的关系
一个CLR可以包含多个AppDomain
3、CLR和线程池的关系
一个CLR只包含一个线程池
所以得出一个CLR下的多个AppDomain共享一个线程池和一个进程下的多个CLR拥有多个线程池的结论.注:多个线程池间的线程池相互不产生影响.
4、CLR和线程池和操作请求队列的关系
(1)、CLR第一次初始化时,线程池并没有线程,当应用程序调用异步代码执行一个方法时,会将该请求记录项加入到操作请求队列中,线程池的代码从这个队列中获取记录项,并派发给线程池线程,接着
线程池会创建线程,当然这里会有性能开销,但是当该线程执行完毕之后,线程池会回收这个线程,这里注意:线程池不会直接销毁这个线程,而是让它处于闲置状态.这样就不会产生额外的性能开销.
但是如果该线程如果长时间处于闲置状态,那么线程池会销毁它,关于这个时间的计算很复杂,各个CLR对它的定义各不相同.
(2)、当应用程序向线程池发起了多个请求,线程池会尝试用一个线程来处理你所有的请求,但是如果这个线程处理压力过大,那么它会开启一个新的线程来给它分担压力.以此类推.
(3)、线程池之包含了少量线程,因为如果线程太多,会增加性能开销,当然如果你升级了你电脑的cpu,线程池则会创建更多的线程.这个过程线程池会自动的去读取你得cpu核数信息,自动的去分配合适的线程数
合理地分配CPU资源.当应用程序的压力减轻,那么它会销毁不用的线程.
(4)、代码演示
static void Main(string[] args)
{
Console.WriteLine("主线程开始执行,调用一个带参数的线程池子线程");
//主线程调用ThreadPool.QueueUserWorkItem方法向线程池的操作队列添加一个记录项
//线程池会遍历这个操作队列的所有记录项,然后将记录项中派发给一个线程池线程
//接着线程池的线程就开始执行ExecuteOtherWork方法(同时接受了主线程传递给它的参数)
ThreadPool.QueueUserWorkItem(ExecuteOtherWork,);
Console.WriteLine("主线程继续执行");
Console.WriteLine("两个线程全部执行完毕");
Console.Read();//这行代码必须加,因为线程池是后台线程,当进程关闭,该进程所有的后台线程都会被关闭,不管是否执行完毕.
} /// <summary>
/// 线程池子线程调用的方法
/// </summary>
/// <param name="state"></param>
static void ExecuteOtherWork(object state)
{
Console.WriteLine("线程池子线程开始执行,主线程传递给它的参数是:{0}", state);
Console.WriteLine("线程池子线程执行完毕");
}

注:这里的输出顺序不确定,因为在多核机器下,可能线程调度器会同时执行主线程和子线程.
四、关于线程池线程的执行上下文
(1)、什么是执行上下文
执行上下文是初始线程的环境描述的数据结构,该结构包含以下东西:
i、安全设置(压缩栈、Thread的Principal属性( 获取或设置线程的当前负责人(对基于角色的安全性而言))和Windows身份)
ii、宿主设置 详情参见HostExecutionContext、HostExecutionContextManager类,通过该类可以设置宿主上下文的状态、以及创建当前宿主上下文的副本.代码,并设置子线程的上下文为主线程的上下文:
static void Main(string[] args)
{
Console.WriteLine("主线程开始执行,调用一个带参数的线程池子线程");
var mainThreadContext = new HostExecutionContext("");
mainThreadContext=mainThreadContext.CreateCopy();
var thread = new Thread(ExecuteOtherWork);
thread.Start(mainThreadContext);
Console.Read();
} /// <summary>
/// 线程池子线程调用的方法
/// </summary>
/// <param name="state"></param>
static void ExecuteOtherWork(object state)
{
var manager = new HostExecutionContextManager();
manager.SetHostExecutionContext(state as HostExecutionContext);
Console.WriteLine("子线程执行完毕");
}
凑合着看,暂时还没有发现这么做的实际意义.可能只有微软知道。哈哈!CLR默认造成初始线程的上下文流向任何子线程。

注:当初始线程创建多个辅助线程时,线程池默认辅助初始线程的上下文,并复制给所有的辅助线程.关于上下文复制的这种机制,很清楚,肯定会造成性能上的开销,每开启一个新的线程就会复制原有线程的上下文给新的线程.
但是考虑到性能问题,MS提供了ExecutionContext取消上下文复制的这种机制.
private static string shareKey = "线程之间共享的数据槽值键";
static void Main(string[] args)
{ Console.WriteLine("主线程开始执行,调用一个带参数的线程池子线程");
CallContext.LogicalSetData(shareKey, "");
ExecutionContext.SuppressFlow();
var thread = new Thread(ExecuteOtherWork);
thread.Start();
Console.Read();
} /// <summary>
/// 线程池子线程调用的方法
/// </summary>
/// <param name="state"></param>
static void ExecuteOtherWork(object state)
{
Console.WriteLine("线程之间共享的数据槽值:{0}", CallContext.LogicalGetData(shareKey));
Console.WriteLine("子线程执行完毕");
}

关于CallContext.LogicalSetData参考下面的例子
iii、逻辑调用上下文数据结构CallContext类,关于它的用法,如下:
private static string notShareContextKey = "线程内唯一的对象,无法共享到其他线程";
private static string shareContextKey = "线程之间共享的对象,可以传播到其他线程";
static void Main(string[] args)
{
Console.WriteLine("主线程开始执行,调用一个带参数的线程池子线程");
CallContext.SetData(notShareContextKey, "");
CallContext.LogicalSetData(shareContextKey, "");
var thread = new Thread(ExecuteOtherWork);
thread.Start();
Console.WriteLine("看看主线程能不能通过CallContext.SetData方法拿到这个数据:{0}", CallContext.GetData(notShareContextKey) ??"没有拿到");
Console.WriteLine("看看主线程能不能通过CallContext.LogicalSetData方法拿到主线程的逻辑上下文对象里面设置的数据:{0}", CallContext.LogicalGetData(shareContextKey) ?? "没有拿到");
Console.Read();
}
/// <summary>
/// 线程池子线程调用的方法
/// </summary>
/// <param name="state"></param>
static void ExecuteOtherWork(object state)
{
var obj=CallContext.GetData("线程内唯一的对象,无法共享到其他线程");
Console.WriteLine("看看子线程能不能通过CallContext.SetData方法拿到主线程的逻辑上下文对象里面设置的数据:{0}", CallContext.GetData(notShareContextKey) ?? "没有拿到");
Console.WriteLine("看看子线程能不能通过CallContext.LogicalSetData方法拿到主线程的逻辑上下文对象里面设置的数据:{0}", CallContext.LogicalGetData(shareContextKey) ?? "没有拿到");
Console.WriteLine("");
Console.WriteLine("子线程执行完毕");
}
CallContext.SetData设置的数据线程内唯一,不能跨线程调用,但是CallContext.LogicalSetData可以跨线程调用.后者类似于HttpContext的Session机制,用于保存用户信息,不受多线程的影响,如果你希望你的数据随着线程的消失而消失可以使用前者来做,其实HttpContext上下文的本质就是使用CallContext,我推测的,没有检验,但是效果是一样的,本身用户的请求就相当于一个线程.所以,可以通过对这两者的理解,可以封装一个对象,该对象维持一个应用程序上下文,同时能满足Web应用,可其他基于线程池的应用.即使在多线程环境下,也能很好的维护一些应用全局共享的关键数据.具体请参考大内老A的文章http://www.cnblogs.com/artech/archive/2010/02/25/1673792.html,写的很好.关于两个方法在代码层面的区别.这里我就不演示了.
C# 多线程学习系列三之CLR线程池系列之ThreadPool的更多相关文章
- Java多线程高并发学习笔记(三)——深入理解线程池
线程池最核心的一个类:ThreadPoolExecutor. 看一下该类的构造器: public ThreadPoolExecutor(int paramInt1, int paramInt2, lo ...
- 浅谈线程池(上):线程池的作用及CLR线程池
原文地址:http://blog.zhaojie.me/2009/07/thread-pool-1-the-goal-and-the-clr-thread-pool.html 线程池是一个重要的概念. ...
- Java多线程学习(三)volatile关键字
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- JavaSE学习笔记(13)---线程池、Lambda表达式
JavaSE学习笔记(13)---线程池.Lambda表达式 1.等待唤醒机制 线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用 ...
- Android(java)学习笔记267:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- 线程机制、CLR线程池以及应用程序域
最近在总结多线程.CLR线程池以及TPL编程实践,重读一遍CLR via C#,比刚上班的时候收获还是很大的.还得要多读书,读好书,同时要多总结,多实践,把技术研究透,使用好. 话不多说,直接上博文吧 ...
- Android(java)学习笔记211:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- 并发编程学习笔记(14)----ThreadPoolExecutor(线程池)的使用及原理
1. 概述 1.1 什么是线程池 与jdbc连接池类似,在创建线程池或销毁线程时,会消耗大量的系统资源,因此在java中提出了线程池的概念,预先创建好固定数量的线程,当有任务需要线程去执行时,不用再去 ...
随机推荐
- Django继承AbstractUser新建User Model时出现fields.E304错误
错误内容如下 ERRORS: audit.UserProfile.groups: (fields.E304) Reverse accessor for 'UserProfile.groups' cla ...
- 好文推荐系列---------(4)使用Yeoman自动构建Ember项目
好文原文地址:http://segmentfault.com/a/1190000000368881 我决定学习前端开发的效率工具Yeoman.本文将首先介绍Yeoman的基本情况,接着我们会使用Yeo ...
- jquery页面隐藏和展开之间切换
html页面: <p id="myp4">默认情况下,这段话是隐藏的,点击按钮以后,这段话就展开,并且按钮上的值改变</p> <button id=& ...
- 从点到面,给Button的属性动画
属性动画是API 11加进来的一个新特性,其实在现在来说也没什么新的了.属性动画可以对任意view的属性做动画,实现动画的原理就是在给定的时间内把属性从一个值变为另一个值.因此可以说属性动画什么都可以 ...
- HTML5表单新属性
HTML5表单新属性 autofocus 自动聚焦 <input type="text" autofocus> placeholder占位文本 tel ...
- PAT甲级 1121. Damn Single (25)
1121. Damn Single (25) 时间限制 300 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue "Dam ...
- 17、文件IO详解及实例
上篇文章已经讲过了文件系统的一些基本的概念,这里首先对文件IO进行详细的学习,文件IO也称为系统调用IO,是操作系统为"用户态"运行的进程和硬件交互提供的一组接口,即操作系统内核留 ...
- python之随机验证码
一.内置函数 chr ------把数字转换为字母 ord-------把字母转换为数字 n = chr(65) print (n) m = ord("A") print(m) 二 ...
- 2.启动MySql服务
windows10下启动mysql服务出现服务名无效的原因及解决方法 问题原因:mysql服务没有安装. 解决办法: 在 mysql bin目录下 以管理员的权限 执行 mysqld -install ...
- [leet code 198]House Robber
1 题目 You are a professional robber planning to rob houses along a street. Each house has a certain a ...