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方法从文件中读取数据(此时线程从托管代码转为本地/用户模式代码)- ...
随机推荐
- 打开SQL2008R2配置工具,提示远程调用失败[0x800706be]
卸载了Microsoft SQL Server 2012 Express LocalDB,依然不行. 再卸载Microsoft SQL Server 2014 LocalDB,此时右边显示框已可以显示 ...
- WPF Item拖拽 DragDrop
今天有个需求是需要拖拽DataGrid中的item到另一个DataGrid.自己实现还比较繁琐,网上查了查,发现一个不错的开源项目 gong-wpf-dragdrop nuget安装下:Install ...
- 使用cropper插件进行图片裁剪 并上传
cropper插件的使用和 github地址: github 官方实例 我参考的中文文档: https://www.cnblogs.com/baka-sky/p/8001577.html 因为我是.n ...
- Day6 ,周期末考试试题
Python基础数据类型考试题 考试时间:两个半小时 满分100分(80分以上包含80分及格) 一,基础题. 1,简述变量命名规范(3分) 2,字节和位的关系 ...
- const的详解
1.const的成员变量 常成员变量的值不能被更新,将在构造函数时候进行初始化 2.const的成员函数 常成员函数只能调用常成员函数,常成员函数不能修改任何成员变量的数值 3.const的成员对象 ...
- Console命令,让js调试更简单
一.显示信息的命令 console.log("normal"); // 用于输出普通信息 console.info("information"); // 用于输 ...
- Python中获取当前时间 获取当前时间前几天的代码
当然 需要引入 datetime import datetime 获取当前日期:datetime.datetime.now().strftime('%Y-%m-%d') 获取当前日期前七天日期: no ...
- java求三角形面积以及周长---封装
/*时间: 2012-10-08作者: 烟大程序要求: 1.封装一类三角形对象Triangle,该类对象具有三条边的属性, 具有初始化三角形的功能.修改边长的功能.判断三条边能否构成三角形的功能. 求 ...
- 【hdu6121】 Build a tree 简单数学题
题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6121 我好像推得挺久的诶..... 题目大意:给你一棵有$n$个点的树,根节点为$0$,对于其余节点 ...
- Linux CentOS7系统探索
这两天,突发奇想,想着用着微软家的windows系统很多年了,也想尝试一下其他的操作系统.很快的就想到了Linux操作系统,它不是面向用户的,而是面向服务器的,在服务器端的市场中占了很大的市场份额,备 ...