CLR via C# 读书笔记-26.线程基础
前言
这俩个月没怎么写文章做记录分享,一直在忙项目上线的事情,但是学习这件事情,停下来就感觉难受,clr线程这章也是反复看了好多遍,书读百遍其义自见,今天我们来聊下线程基础
1.进程是什么,以及线程起源
2.线程开销,以及上线文切换
3.使用线程的理由
4.线程调度和优先级
5.前台线程和后台线程
一、进程是什么,以及线程起源
在计算机的早期岁月,os没有线程的概念,整个系统只运行者一个执行线程,其中包含操作系统和应用程序的代码。这意味着长时间运行的任务会阻止其他任务的运行。在16位windows的那些日子,打印文档的应用程序很容易“冻结”整个机器,造成os和其他应用程序停止响应。遇到这个问题,用户只能按Reset建或重启计算机,所有正在运行的应用程序都会终止,造成应用程序正在处理的数据都会无端的丢失,用户对此深恶痛绝。
Microsoft由此设计出新的OS内核,决定在一个进程中运行应用程序的每个实例,进程实际是 应用程序的实例要使用的资源的集合。每个进程都赋予了一个虚拟的地址空间,确保在一个进程中使用的代码和数据无法由另一个进程访问,进程也访问不了OS的内核代码和数据,所以,应用程序破坏不了其他应用程序或者OS自身,用户体验变得更好了。
听起来不错,但CPU本身呢?应用程序发生死循环会发生什么?如果机器只有一个CPU,它会执行死循环,虽然数据无法被破坏,但系统仍然可能停止响应。Microsoft拿出的解决方案就是线程。作为一个Windows概念,线程的职责是对CPU进行虚拟化。Windows为每个进程都提供了该进程专用的线程(功能相当于一个CPU)。应用程序的代码进入死循环,与那个代码关联的进程会“冻结”,但其他进程(他们有自己的线程)不会冻结,他们会继续执行!
二、线程开销
1.线程内核对象(thread kernel object)
OS为系统中创建的每个线程都分配并初始化这种数据结构之一。数据结构包含一组对线程进行描述的属性。线程结构还包含所谓的线程上下文(thread context)。上下文是包含CPU寄存器集合的内存块。对于x86,x64和ARM CPU架构,线程上下文分别使用约700,1240和350字节的内存
2.线程环境快(thread environment block,TEB)
TEB是在用户模式(应用程序代码能快速访问的地址空间)中分配和初始化的内存块。TEB耗用一个内存页(x86,x64,和ARM CPU中是4KB)。TEB包含线程的异常处理链首(head)。线程进入的每个try块都在链首插入一个节点(node);线程退出try块时从链中删除该节点。此外,TEB还包含线程的“线程本地存储”数据,以及GDI(Graphics Device Interface,图形设备接口)和OpenGL图形使用的一些数据结构
3.用户模式栈(user-mode stack)
用户模式栈存储传给方法的局部变量和实参。它还包含一个地址;指出当前方法返回时,线程应该从什么地方接着执行。Windows默认为每个线程的用户模式栈分配1MB内存。
4.内核模式栈(kernel-mode-stack)
应用程序代码向操作系统中的内核模式函数传递实参时,还会使用内核模式栈。由于对安全的考虑,针对从用户模式的代码传给内核的任何实参,Windows都会把它们从线程的用户模式栈复制到线程的内核模式栈。一经复制,内核就可验证实参的值。由于应用程序代码不能访问内核模式栈,所以应用程序无法更改验证后的实参值。OS内核代码开始处理复制的值。除此之外,内核会调用它自己内部的方法,并利用内核模式栈传递它自己的实参、存储函数的局部变量以及存储返回地址。
4.DLL线程连接(attach)和线程分离(detach)通知
Windows的一个策略是,任何时候在进程中创建线程,都会调用进程中加载的所有非托管DLL的DllMain方法,并向该方法传递DLL_THREAD_ATTACH标志。类似地,任何时候线程终止,都会调用进程中的所有非托管DLL的DllMain方法,并向方法传递DLL_THREAD_DETACH标志。有的DLL需要获取这些通知,才能为进程中创建/销毁的每个线程执行特殊的初始化或(资源)清理操作。
也就是说线程创建销毁时会调用所有DLL中的这个函数,严重影响了进程中创建和销毁线程的性能
上下文切换
单CPU计算机一次只能做一件事情。Windows必须在系统中的所有线程(逻辑CPU)之间共享物理CPU。
Windows任何时刻只将一个线程分配给一个CPU 。那个线程能运行一个“时间片”(有时也成为“量”或者“量程”,即quantum)的长度。时间片到期,Windows就上下文切换到另一个线程。每次上下文切换都要求windows执行以下操作。
1.将CPU寄存器的值保存到当前正在运行的线程的内核对象内部的一个上下文结构中。
2.从现有线程集合中选出一个线程供调度。
3.将所选上下文结构中的值加载到CPU的寄存器中。
上下文切换完成后,CPU执行所选的线程,直到它的时间片到期。然后发生下次上下文切换。Windows大约每30毫秒执行一次上下文切换。
要构建高性能应用程序和组件,就应该尽量避免上下文切换。
减少线程的数量也会显著提升垃圾回收的性能。
三、使用线程的理由
1.可响应性(通常是对于客户端GUI应用程序)
例如windows中每个进程提供它自己的线程,确保发生死循环的应用程序不会妨碍其它应用程序。类似地,在客户端GUI应用程序中,可以将一些工作交给一些线程进行,使GUI线程能灵敏的响应用户输入。
2.性能(对于客户端和服务端应用程序)
由于windows每个CPU调度一个线程,而且多个CPU能并发执行这些线程,所以同时执行多个操作能提升性能。
满足以下任何条件,就可考虑显式创建自己的线程
1.线程需要以非普通线程优先级运行
2.需要线程表现为一个前台线程,防止应用程序在线程结束任务前终止
3.计算限制的任务需要长时间运行
4.要启动的线程,并可能调用Thread的abort方法来提前终止它
四、线程调度和优先级
每个线程都分配了从0(最低)到31(最高)的优先级。
只要存在可调度的优先级31的线程,系统就永远不会将优先级0~30的任何线程分配给CPU。这种情况成为饥饿。
系统启动时会创建一个特殊的零页线程(zero page thread)。该线程的优先级为0,而且是整个系统唯一优先级为0的线程。在没有其他线程需要“干活儿”的时候,零页线程将系统RAM的所有空闲页清零。
五、前台线程和后台线程
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//创建新线程(默认为前台线程)
Thread t = new Thread(Worker);
//使线程成为后台线程
t.IsBackground = true;
//启动线程
t.Start();
//如果t是前台线程,则应用程序大概10秒后才终止
//如果t是后台线程,则应用程序立即终止
Console.WriteLine("Returing from Main");
} private static void Worker()
{
//模拟做10秒钟的工作
Thread.Sleep();
//下面这行代码只有由一个前台线程执行时才会显示
Console.WriteLine("Returning from Worker");
}
}
}
应用程序的主线程以及通过构造一个Thread对象来显式创建的任何线程都默认为前台线程。相反,线程池线程默认为后台线程。
另外,由进入托管执行环境的本机(native)代码创建的任何线程都被标记为后台线程。
天道酬勤,大道至简,坚持。
CLR via C# 读书笔记-26.线程基础的更多相关文章
- 《CLR via C#》读书笔记 之 线程基础
第二十五章 线程基础 2014-06-28 25.1 Windows为什么要支持线程 25.2 线程开销 25.3 停止疯狂 25.6 CLR线程和Windows线程 25.7 使用专用线程执行异步的 ...
- [Clr via C#读书笔记]Cp4类型基础
Cp4类型基础 Object类型 Object是所有类型的基类,有Equals,GetHashCode,ToString,GetType四个公共方法,其中GetHashCode,ToString可以o ...
- 《Windows核心编程》读书笔记.Chapter06线程基础
原文链接在印象笔记(效果也好的多):https://app.yinxiang.com/l/AAQlNLnxTPRMAppVr5W0upchipQDDC_FHlU 概要: 现成也有两个组成部分: 现成的 ...
- CLR Via CSharp读书笔记(26) - 计算限制的异步操作
执行上下文: 执行上下文包括安全设置(压缩栈.Thread的Principal属性和Windows身份),宿主设置(System.Threading.HostExecutionContextManag ...
- 《CLR Via C#》读书笔记:26.线程基础
一.线程开销 操作系统创建线程是有代价的,其主要开销在下面列举出来了. 内存开销 线程内核对象 拥有线程描述属性与线程上下文,线程上下文占用的内存空间为 x86 架构 占用 700 字节.x64 架构 ...
- (CLR via C#学习笔记)异步操作 - 线程池
一 线程池基础 1.线程池维护了一个操作请求队列,将请求的操作追加到线程池队列中,线程池的代码从队列中提取操作项,派发给线程池中的线程; 2.CLR初始化时,线程池中是没有线程的,当有操作派发给线程池 ...
- CLR via C# 读书笔记-21.托管堆和垃圾回收
前言 近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点. 1,.托管代码和非托管代码的区别 ...
- Java并发读书笔记:线程安全与互斥同步
目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...
- Clr Via C#读书笔记---I/O限制的异步操作
widows如何执行I/O操作 构造调用一个FileStream对象打开一个磁盘文件-----FileStream.Read方法从文件中读取数据(此时线程从托管代码转为本地/用户模式代码)- ...
随机推荐
- [工具]JSON校验、转换在线工具
1. 在线JSON代码检验.检验.美化.格式化工具[简单易用的格式化工具]: http://tools.jb51.net/code/json 2. JSON在线格式化工具[代码高亮及可控缩进大小的格式 ...
- .NET 简易方法拦截器
伟大的无产阶级Willaim曾说过:"无论你觉得自己多么的了不起,也永远有人比你更强".对,我说过!我就是william. 今天想记录一下在项目中遇到的一个比较有意思的东西,异常拦 ...
- [Xamarin]我的Xamarin填坑之旅(一)
一想到明天是星期五,不对,是今天,心里就很激动,毕竟明天没课.激动之余,来写一篇博客,记录一下最近踏坑Xamarin开发校园助手APP的一些事儿.也许更像是一篇流水账. 在扯Xamarin之前,有必要 ...
- foreach写失效的问题
本文由作者张远道授权网易云社区发布. 坦白讲身为程序员,bug在所难免.有人讲,bug越多,说明程序员越伟大.这句话有它一定的道理. 因为从某方面讲,bug多了说明他的代码量也多. 言归正传,这里我记 ...
- 栈(顺序栈)----C语言
栈 栈是一种运算受限的线性表,是一种先进后出的数据结构,限定只能在一端进行插入和删除操作,允许操作的一端称为栈顶,不允许操作的称为栈底 顺序栈(顺序结构) 顺序栈:用一段连续的存储空间来存储栈中的数据 ...
- http 协议 c++代码 获取网页
最近接触了些 html 和 JavaScript,索性了解下 http 协议.在园子里找到了 HTTP协议详解,图文并茂,很爽! 于是小小的尝试了下 #include <WinSock2.h&g ...
- [JS深入学习]——数组对象排序
(转) JavaScript实现多维数组.对象数组排序,其实用的就是原生的sort()方法,用于对数组的元素进行排序. sort() 方法用于对数组的元素进行排序.语法如下: arrayObject. ...
- Jmeter服务器监控 serveragent如何使用
安装jmeter插件Plugins Managerjmeter-plugins.org推出了全新的Plugins Manager,对于其提供的插件进行了集中的管理,我们只需要安装这个管理插件,即可以在 ...
- 一个自动换行,不可以滚动的 textview
 主要效果有几点 只显示一行文字 输入文字过长时,自动换行 上下不可以滑动 删除时,自动显示上一行文字. 如何做到 只显示一行 textView.heightAnchor.constraint(eq ...
- Java之Socket网络编程实践
转自:http://my.oschina.net/leejun2005/blog/104955#comments 一.TCP/IP协议 既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准 ...