一、线程开销

操作系统创建线程是有代价的,其主要开销在下面列举出来了。

内存开销

  1. 线程内核对象

    拥有线程描述属性与线程上下文,线程上下文占用的内存空间为 x86 架构 占用 700 字节、x64 架构 1240 字节 、ARM 架构 350 字节。

  2. 线程环境块(TEB)

    TEB 消耗一个内存页,占用 4KB内存。

  3. 用户模式栈。

    用户模式栈存储传递给方法的局部变量与实参,并且还存储有一个地址用于当前方法返回的时候,线程应该从哪个地方继续执行。默认 Windows 分配保留 1MB 内存。

  4. 内核模式栈。

    32 位 Windows 占用 12 KB,64 位 Windows 占用 24 KB。

  5. DLL 线程连接与线程分离通知。

    这种策略只有 Windows 才会存在,当创建线程时, Windows 会调用进程所有非托管 DLL 的 DllMain 方法,并未其传递 DLL_THREAD_ATTACH 标志,线程终止时传递 DLL_THREAD_DETACH 标志。

线程上下文切换与 CPU 之间的关系

Windows 在任何时刻都只会将 1 个线程分配给 1 个 CPU ,该线程享有一个时间片的运行时间。时间片到期之后,Windows 会将上下文切换到另外一个线程,动作如下:

  1. 将 CPU 寄存器值存储在当前正在运行的线程的内核对象内部的上下文结构之中。
  2. 从先有线程集合选取一个线程供调度,如果该线程属于另一个进程,还得切换 CPU 能够操作的虚拟地址空间。
  3. 将上下文结构中的值加载到 CPU 寄存器之中。

以上操作做完之后,Windows 等待这个线程时间片到期,执行下次切换,每次切换的时间开销大概为 30 毫秒。

如果一个线程时间片结束之后,下一个调度的线程还是之前的线程则不会产生线程上下文切换。

所以在理想状态下,每个系统最佳的线程数应该与其核心数相同,(如果是 4 核 8 线程则最优应该为 8 个)因为这样上下文切换出现的情况就会少很多。

最重要的是,Windows 系统上大部分程序线程都处于空闲状态,但是线程占用的内存空间是事实存在的。

三、使用专用线程执行计算限制的异步操作

一般来说不推荐使用 Thread 手动创建线程,而应该使用线程池,不过在有以下需求时,可以手动创建线程。

  1. 需要设定更高的线程优先级的时候。
  2. 需要将线程设置为前台线程。
  3. 某些长耗时的专用线程。
  4. 该线程可能会通过 Thread 的 Abort 方法终止自身。

在调用过程中,如果使用了 Thread.Join() 方法那么就会造成调用线程阻塞当前代码,直到创建的线程被终止。

四、为什么要使用线程

  1. 针对于客户端程序而言,多线程可以增强响应性,不会因为耗时操作阻塞 UI 线程造成用户体验卡顿。
  2. 针对于服务器程序而言,可以并发地处理用户请求,充分利用多核 CPU 的优势。

作者的观点是,计算机的 CPU 使用率应该保持 100% 的使用率才不算是浪费计算资源。

五、线程调度与优先级

抢占式系统通过优先级来判定线程在什么时候调度多少时间,每个线程都分配了从 0 到 31 的优先级,系统为 CPU 分配线程时,首先检查 31 的线程,并以轮询的方式调度他们(优先级都为 31)。

如果高优先级的线程一直处于调度状态,那么操作系统不会将 CPU 分配给低优先级的线程,这样就会造成 线程饥饿

较高的优先级线程总会抢占低优先级线程,即便该线程的时间片没有用完。

CPU 会创建一个优先级为 0 的 零页线程 ,该线程是系统唯一一个优先级为 0 的线程,只有在 CPU 空闲的时候会执行他,用于清理 RAM 中所有的空闲内存页。

【注意】

进程优先级类 + 线程优先级构成了一个基础优先级,Windows 还有一个动态优先级用于防止产生线程饥饿,会动态调成线程的优先级状态。

但是动态优先级只会针对基础优先级在 0 ~ 15 的线程应用,16 ~ 31 不受这个管控。

Windows 通过两个抽象层用于表示进程优先级类和线程优先级,单一般 C# 用户代码中能够控制的只有线程优先级,他们分别是:Lowest、BelowNormal、Normal、AboveNormal、Highest。

六、前台线程与后台线程

在 CLR 中线程只有两种状态,前台线程和后台线程,而且当所有前台线程被终止之后,CLR 会强行关闭所有后台线程,并退出程序。

线程在运行的生命周期当中可以变更其状态,但主线程默认为前台线程,使用 Thread 类型创建的线程默认也是前台线程。只有线程池的线程默认为后台线程,进入托管执行的本机代码创建的任何线程也会标记为后台线程。

《CLR Via C#》读书笔记:26.线程基础的更多相关文章

  1. CLR via C# 读书笔记-26.线程基础

    前言 这俩个月没怎么写文章做记录分享,一直在忙项目上线的事情,但是学习这件事情,停下来就感觉难受,clr线程这章也是反复看了好多遍,书读百遍其义自见,今天我们来聊下线程基础 1.进程是什么,以及线程起 ...

  2. 《CLR via C#》读书笔记 之 线程基础

    第二十五章 线程基础 2014-06-28 25.1 Windows为什么要支持线程 25.2 线程开销 25.3 停止疯狂 25.6 CLR线程和Windows线程 25.7 使用专用线程执行异步的 ...

  3. [Clr via C#读书笔记]Cp4类型基础

    Cp4类型基础 Object类型 Object是所有类型的基类,有Equals,GetHashCode,ToString,GetType四个公共方法,其中GetHashCode,ToString可以o ...

  4. 《Windows核心编程》读书笔记.Chapter06线程基础

    原文链接在印象笔记(效果也好的多):https://app.yinxiang.com/l/AAQlNLnxTPRMAppVr5W0upchipQDDC_FHlU 概要: 现成也有两个组成部分: 现成的 ...

  5. CLR Via CSharp读书笔记(26) - 计算限制的异步操作

    执行上下文: 执行上下文包括安全设置(压缩栈.Thread的Principal属性和Windows身份),宿主设置(System.Threading.HostExecutionContextManag ...

  6. (CLR via C#学习笔记)异步操作 - 线程池

    一 线程池基础 1.线程池维护了一个操作请求队列,将请求的操作追加到线程池队列中,线程池的代码从队列中提取操作项,派发给线程池中的线程; 2.CLR初始化时,线程池中是没有线程的,当有操作派发给线程池 ...

  7. CLR via C# 读书笔记-21.托管堆和垃圾回收

    前言 近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点. 1,.托管代码和非托管代码的区别 ...

  8. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

  9. Clr Via C#读书笔记---I/O限制的异步操作

    widows如何执行I/O操作      构造调用一个FileStream对象打开一个磁盘文件-----FileStream.Read方法从文件中读取数据(此时线程从托管代码转为本地/用户模式代码)- ...

随机推荐

  1. Linux学习笔记:Jenkins的使用(二)

    一些插件的使用 Deploy to container Plugin jenkins安装完成之后,添加插件 Deploy to container Plugin ,这个插件可以将打好的war包部署到t ...

  2. Quartz的自定义插件

    quartz本身插件: LoggingJobHistoryPlugin,LoggingTriggerHistoryPlugin分别可以打印scheduler容器管理的所有triggers和jobDet ...

  3. 别人的Linux私房菜(13)学习Shell脚本

    CentOS6.x以前版本的系统服务启动接口在/etc/init.d/目录下,存放了脚本. Shell脚本因调用外部命令和bash 的一些默认工具,速度较慢,不适合处理大量运算. 执行方式有:直接命令 ...

  4. 字符串、数组、对象常用API

    常用的字符串API  1.常见方法和属性 length 属性,获取字符串的字符数量 charAt(i) 返回给定位置的字符 charCodeAt( ) 返回给定位置的字符的字符编码 <scrip ...

  5. easyui_validatebox常用验证

    $.extend($.fn.validatebox.defaults.rules, { idcard: {// 验证身份证 validator: function (value) { return / ...

  6. 解决mysql for docker容器报错:Authentication plugin 'caching_sha2_password' cannot be loaded

    为图方便,懒得在mac上安装mysql了,一个是管理不方便,第二个是为了方便多机器同步开发环境.就使用docker安装了. 拉取mysql镜像 docker pull mysql 运行mysql实例 ...

  7. mpdf-html转PDF,中文字符乱码、加粗问题

    $defaultConfig = (new ConfigVariables())->getDefaults(); $fontDirs = $defaultConfig['fontDir']; $ ...

  8. beego笔记

    beego学习笔记一:创建第一个beego Web项目 Go语言beego框架快速搭建体验五分钟讲解01 beego框架图文简介五分钟讲解02 beego框架图文简介五分钟讲解03-go语言简单方式操 ...

  9. c++中二级指针的使用场景

    二级指针的使用场景如下: 1.主要用来为指针变量分配内存空间: void GetMemory(char **p) { *p = ]; } 函数调用方式: char *str = NULL; GetMe ...

  10. redis设置远程通过密码进行连接

    个人配置:服务器+本地鸡+win 文件概况.↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 首先修改第一个redis.windows.conf 56行左右,将bind 127.0.0.1注释掉(行前面加上#    ...