线程详解

以下内容摘自《程序员的自我修养》

什么是线程?

线程(Thread),有时被称为轻量级(Lightweight Process, LWP),是程序执行流程的最小单元。一个标准的线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成 。通常意义上,一个进程由一个到多个线程,各个线程之间共享程序的内存空间(包括代码段、数据段、堆等)及一些进程级的资源(如打开文件和信号)。一个经典的线程与进程的关系如图:

大多数软件应用中,线程的数量都不止一个。多个线程可以互不干扰地并发进行,并共享进程的全局变量和堆的数据。那么,多个线程与单线程的进程相比,又有哪些优势呢?通常来说,使用多线程的原因有如下几点:

  • 某个操作可能会陷入长时间等待,等待的线程会进入睡眠状态,无法继续执行。多线程执行可以有效利用等待的时间。
  • 某个操作(常常是计算)会消耗大量的时间,如果只有一个线程,程序和用户之间的交互会中断。多线程可以让一个线程负责交互,另一个线程负责计算。
  • 程序逻辑本身要求并发操作,例如一个多端下载软件
  • 多CPU或多核计算机,本身具备同时执行多个线程的能力,因此单线程程序无法全面发挥计算机的全部计算能力。
  • 相对于多进程应用,多线程在数据共享方面的效率高得多。

线程访问权限

线程的访问非常自由,它可以访问进程内存里的所有数据,甚至包括其他线程的堆栈(如果它知道其他线程的堆栈地址,那么就是很少见的情况),但实际运用中线程也拥有自己的私有存储空间,包括以下几方面:

  • 线程局部存储
  • 寄存器

线程调度与优先级

不论是在多处理器的计算机上还是单处理器的计算机上,线程总是“并发”执行的。当线程的数量小于等于处理器的数量时(并且操作系统支持多处理器),线程的并发是真正的并发,不同的线程运行在不同的处理器上,彼此之间互不相干。但对于线程数量大于处理器的情况,线程的并发会受到一些阻碍,因此此时至少一个处理器会运行多线程。 在单处理器对应多线程的情况下,并发是一种模拟出来的状态。操作系统会让这些多线程程序轮流执行,每次仅执行一小段时间,(通常是几十到几百毫秒),这样每一个线程就“看起来”在同时执行。这样的一个不断在处理器上切换不同的线程的行为称之为线程调度。在线程调度中,线程通常拥有三种状态,分别是:

  • 运行(Running)
  • 就绪(Ready)
  • 等待(Waiting) 处于运行中的线程拥有一段可执行的时间,这段时间称为时间片。当时间片用尽后的时候,进程就开始等待某事件,那么它将进入等待状态。每当一个线程离开运行状态时,调度系统就会选择一个其他的就绪线程继续执行。在一个处于等待状态的线程锁等待的事件发生之后,该线程就将进入就绪状态;

线程调度自多任务操作系统问世以来,就不断被提出不同的方案和算法,现在主流的调度方式尽管各不相同,但都带有优先级调度和轮转法。 在windows中可以通过

BOOL WINAPI SetThreadPriority(HANDLE hThread, int nPriority);

来设置线程的优先级,而linux下与线程相关的操作可以通过pthread库来实现;

让我们总结一下,在优先级调度的环境下,线程的优先级改变一般有三种方式。

  • 用户指定优先级
  • 根据进入等待状态的频繁程序提升或降低优先级
  • 长时间得不到执行而被提升优先级

可抢占线程和不可抢占线程

我们之前讨论的线程调度有一个特点,那就是线程在用尽时间片之后会被强制剥夺继续执行的权利,而进入就绪状态,这个过程叫做抢占。 在不可抢占的线程中,线程主动放弃执行无非是两种情况。

  • 当线程试图等待某一事件时(I/O等)。
  • 线程主动放弃时间片 因此,在不可抢占线程执行时候,有一个显著的特点,那就是线程调度的时间是确定的。线程调度只会发生在线程主动放弃执行或线程等待某事件的时候。这样可以避免一些因为抢占式线程里调度时间不确定而产生的问题。

Linux的多线程

Windows对进程和线程的实现如同教科书一般标准,windows内核有明确的线程和进程的概念,并且有一系列的API来操纵它们。但对于linux来说,线程并不是一个通用的概念。 Linux对多线程的支持颇为贫乏,事实上,在Linux内核中并不存在真正意义上的线程概念。Linux将所有的执行实体(无论是线程还是进程)都称为任务(Task),每一个任务概念上不同任务之间都可以选择共享空间,因此在实际意义上,共享同一个内存空间的多个任务构成了一个进程,这些任务也就成了这个进程中的线程。在Linux下,用以下方法可以创建一个新的任务:

fork函数产生一个和当前进程完全意义的新进程,并和当前进程一样从fork函数里返回。例如如下代码:

pid_t pid;
if (pid = fork())
{
    ....
}

fork只能够产生本任务的镜像,因此须要使用exec配合才能启动别的新任务。exec可以用新的可执行映像替换当前的可执行镜像

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

Dubbo入门——搭建一个最简单的Demo框架

在这里插播一条关于RPC的简介: RPC(Remote Procedure Call Protocol):远程过程调用: 两台服务器A、B,分别部署不同的应...

12220
来自专栏我和PYTHON有个约会

莱茨狗-Go语言第二弹:环境搭建

两种环境包,根据需要下载 如果你是windows江湖新手,那么下载msi安装包吧,界面图形化鼠标傻瓜式操作,全程安装辅助基本没有问题!

7410
来自专栏逢魔安全实验室

某移动应用安全加固与脱壳技术研究与实例分析

62580
来自专栏吴伟祥

缓存穿透、并发和雪崩那些事 转

作者:李艳鹏,阿里资深技术专家!著有《可伸缩服务架构》,《分布式服务架构》等作品,在区块链,聚合支付,电商等领域有一定的积累!

8430
来自专栏企鹅号快讯

做网站-推荐3种CSS,JS合并的方式

在Web项目的开发中,js,css文件会随着项目的开发变得越来越多,越来越大,这就给给性能方面带来一些问题,如,页面引入的的js,css越多的话,那么对就增加了...

765110
来自专栏友弟技术工作室

云原生概念

2K50
来自专栏前端架构与工程

webpack多页面开发与懒加载hash解决方案

本文内容只适用于webpack v1版本,webpack v2已经修复了hash计算规则。 之前讨论了webpack的hash与chunkhash的区别以及各...

22380
来自专栏Web项目聚集地

Dubbo入门-搭建一个最简单的Demo框架

1. 单一应用框架(ORM) 当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本。 缺点:单一的系统架构,使得在开发过...

1.8K20
来自专栏我就是马云飞

JJEvent 一个可靠的Android端数据埋点SDK

注:代码已经经过线上项目验证, 横向Google统计对比,统计数据无丢失,性能稳定.

45340
来自专栏码神联盟

灵丹妙药 | 关于缓存,你必须要知道的

这两天小编一直在总结缓存的要点,也同时参考了一些文档,仅此奉上,以供参考。 缓存是必备技能 身为后端开发的开发人员,缓存是必备技能。不需要花费太多的精力就能显著...

36470

扫码关注云+社区

领取腾讯云代金券