惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
D
DataBreaches.Net
F
Full Disclosure
腾讯CDC
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - 聂微东
雷峰网
雷峰网
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
O
OpenAI News
Project Zero
Project Zero
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
Schneier on Security
Schneier on Security

博客园 - e旋风

异常码汇总 搬家公司管理系统,能完成车辆管理、订单管理等工作,并给出报表统计数据 Win7系统上华为无线上网卡与VMware冲突导致驱动无法正常安装的解决办法 VMware 中禁用虚拟内存加速虚拟机速度 [转]WinForm数据绑定--BindingContext C#中字符数与字节数的区别 - e旋风 - 博客园 世联地产软件工程师笔试试题 HttpRuntime.Cache 与HttpContext.Current.Cache的疑问 配置节处理程序声明 - e旋风 - 博客园 判断是否存在在父结果集中有而在子结果集中没有的记录的最佳方法 动态执行Sql语句与临时表的问题(对象名无效) 定义局部变量时,字符串不能超过8000的方法 我搜集的关于工作流方面的技术文章 注意Transact-SQL中Case函数的两种用法导致不同的结果集 只有设置为InProc,Session失效时才会触发Session_End asp.net用户控件中使用相对路径问题 a href=#与 a href=javascript:void(0) 的区别 打开新窗口链接的几种办法 只能输入汉字,数字,英文大小写,符号只允许,。!的正则表达式 HTML及XML语言的转义字符
Thread & ThreadPool 的一些背景知识 - e旋风
e旋风 · 2008-09-28 · via 博客园 - e旋风

       CLR 目前的多线程技术依然是 Windows 操作系统所提供的,不过 .NET CLR 开发小组似乎保留了将其分离的权利。在某些环境,CLR 线程并不会直接映射到一个 Windows 线程上,他们可能会用 Windows fiber 来代替,以期获得更好的执行性能。未来的 CLR 版本甚至会直接用某个已存在的空闲线程来代替 "new Thread()" 执行其他任务。CLR 线程使用了 Windows 线程开发的很多技巧,这样我们可以使用简单的代码来处理原本需要花费很多精力才能完成的工作。这种包装分离带来的另外一个好处就是,我们无需改动我们的代 码就能获得 CLR 和操作系统升级带来的性能提升。

除了直接使用 CLR 线程,我们还可以使用 Thread.BeginThreadAffinity() 等手段直接使用操作系统级别的线程,只不过要记得调用相关方法去 "End"。

早 期的 DOS 和 Windows 16-bit 都是单线程操作系统,这种操作系统上的某个进程一旦陷入死循环,整个操作系统都完蛋,你能做的只有重启计算机。Windows NT 3.1 是微软开发的第一个支持多线程功能的操作系统,这从某种程度上可以说是 Windows 成为 "健壮性" 操作系统的标志。在支持多线程的操作系统里,每个进程都拥有自己的线程,也就是说理论上不会陷入上述那样的尴尬状况了。死循环的线程被冻结,而其他线程依 旧能正常运转,用户也就有机会强行结束那个死掉的家伙。

严格来说,线程是一笔昂贵的开支。创建线程并不简单,首先得分配并初始化一个线程 内核对象(thead kernel object),并为这个线程保留 1MB user-mode stack 和 12KB 以上的 kernel-mode stack。在完成这些之后,线程才被创建。Windows 会发送消息通知目标进程以及其所有 DLL 线程可用。而销毁线程同样需要需要发送消息通知,最后还得释放所有的保留空间。

在单 CPU 计算机上,任何时候都只有一个线程在执行。Windows 保持线程对象状态,并决定接下来哪个线程会被执行。每个线程每次大概可以获得 20 毫秒的 CPU 执行时间片,然后切换执行另外一个线程。这个过程有个专业术语叫 "线程上下文切换(context switch)"。操作系统需要花费相当代价才能走完一次切换:

(1) 进入内核模式。
(2) 将 CPU 寄存器信息保存到当前正在执行的线程内核对象。
(3) 获取一个 Spinlock,按计划决定下一个要执行的线程,然后释放 Spinlock。如果下一个线程属于其他的进程,那么我们还得为虚拟地址交换付出更多代价。
(4) 从要被执行的线程内核对象载入 CPU 寄存器信息。
(5) 离开内核模式。

所 有这些操作可能导致操作系统和应用程序比单线程操作系统执行得更慢,但这些都是值得的,芯片生产商带来的超线程(hyper-threading)和多核 (mulit-core) CPU 为多线程提供了真正的舞台,每个内核上都可以真正并发执行一个线程。超线程 CPU 包含两个逻辑内核,每个逻辑内核都拥有自己的寄存器,只是它们需要共享 CPU 缓存等资源。当一个逻辑 CPU 因某种原因被暂停,芯片会切换到另外一个逻辑 CPU 继续执行任务,超线程芯片能带来 10% - 30% 左右的性能提升。而像 Pentium D、Athlon 64 X2 这类真正的多核 CPU 芯片,它们集成了多个真正意义上的物理内核,每个内核都有自己的完整的寄存器和缓存,这才是 100% 的性能提升。现在某些服务器用的芯片会同时使用多核和超线程技术,因此你可能在任务管理器中看到 4 个或 8 个 CPU 显示。芯片发展已经从单纯的主频提升转移到多核集成上来,不久我们就可以使用 4 核、8 核,甚至是更多更强大的多核处理器。

创建和销 毁线程代价不菲,过多的线程会消耗掉大量的内存和 CPU 资源。为了改善这种状况,CLR 提供了一种称之为 "线程池(thread pool)" 的技术。直观来说,线程池就是为应用程序提供的一堆可用线程集合,线程池在进程所有应用程序域(AppDomain)间共享。

在 CLR 初始化之初,线程池内是没有任何线程的,其内部有一个专门存储请求的队列。当应用程序试图执行异步等操作时,这些方法调用会被包装并加入到线程池队列中。 线程池从队列提取任务请求,并为其分配可用线程。如果线程池内没有可用线程,那么一个新的线程会被创建。线程完成任务执行后,并不会被摧毁。相反,它被放 回到线程池中,然后等待被分配给其他任务。线程池会尝试用同一个线程来处理应用程序的多个任务请求。而一旦应用程序在极短时间内发出多个请求,那么它会尝 试创建额外的线程来分配队列中的任务,这有可能导致池内线程数量急剧增加,同时耗费大量的系统资源。当请求完成,池内多余的线程会在空闲 2 分钟后被释放并回收相关资源,直到某个最小线程阀值设置。

当任务请求超出最小阀值设置,线程池并不会立即创建新线程,而是等待大约 500 毫秒左右。这么做的目的是看看在这段时间内是否有其他工作线程完成任务来接手这个请求,这样就可以避免创建新线程的消耗。最小线程阀值设置 (ThreadPool.SetMinThreads)最好不要小于 CPU 内核数量,否则会导致性能问题。

在线程池内部,它包含两种 类型的线程,分别是 worker thread 和 I/O threads。工作线程用来处理 compute-bound 异步操作(包括初始化 I/O-bound 操作),而 I/O 线程则用于异步执行诸如文件访问、网络通讯、数据库操作、WebService 调用以及某些硬件设备控制等。CLR 允许开发人员设置线程池的最大线程数量,并确保池内线程数量不会超出这个设置。CLR 2.0 ThreadPool 默认为每个 CPU 处理器提供 25 个工作线程以及 1000 个 I/O 线程,通常情况下这已经足够了,并不需要我们做出特别的处理。