https://blog.csdn.net/health747474
背景:
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
-
基于进程的多任务处理是程序的并发执行。
-
线程的多任务处理是同一程序的片段的并发执行。
多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。
C++ 不包含多线程应用程序的任何内置支持。相反,它完全依赖于操作系统来提供此功能。
本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。
代码实例:https://www.cnblogs.com/hhbeast/p/7484877.html
进程和线程的概念
进程: 进程是操作系统资源分配的基本实体
线程: 线程是CPU调度和分配的基本单位
注1:Linux内核其实不区分进程和线程,内核把执行单元叫做任务(task)。操作系统实际上调度的是进程,进程通过fork()来创建同样的另一个进程。每个进程有一个PID,同一组进程中最先启动的那个还有一个TGID。严格来说前者应该叫线程ID,后者应该叫进程ID。Linux里的线程实际上是共享一些资源的一系列进程而已。因此,Linux上进程分3种,内核线程、用户进程、用户线程, 当然如果更严谨的,你也可以认为用户进程和用户线程都是用户进程。
从Linux内核角度区分:
一个进程由于其运行空间的不同, 从而有内核线程和用户进程的区分
内核线程运行在内核空间, 之所以称之为线程是因为它没有虚拟地址空间, 只能访问内核的代码和数据,
用户进程则运行在用户空间, 但是可以通过中断, 系统调用等方式从用户态陷入内核态,也就是用户栈和内核栈的切换。
Linux下的PCB—task_struct结构体
https://blog.csdn.net/qinghe0808/article/details/70148225(详细解析了进程及其结构体)
当一个程序加载到内存当中,计算机系统中就有了一个进程。每个进程在内核中都有一个进程控制块(PCB)来维护自身进程的信息,这个进程控制块(PCB)是为了方便进行进程管理所设置的一个数据结构,里面存放的是进程的相关信息。也就是说,进程管理管理的是PCB。
用户模式和内核模式
为了使操作系用内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。
处理器通常是用某个控制寄存器中的一个模式位来提供这种功能的,该寄存器描述了进程当前享有的特权。
当设置了模式位时,进程就运行在内核模式中,即超级用户模式。
一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中任何存储器位置。
没有设置模式位时,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令,比如停止处理器,改变模式位,或者发起一个I/O操作。也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。
运行应用程序代码的进程初始时是在用户模式中的。
进程从用户模式变为内核模式的唯一方法是通过诸如中断,故障或者陷入系统调用这样的异常,当异常发生时,控制传递到异常处理程序,处理器将模式从用户模式变为内核模式。
处理程序运行在内核模式中,当他返回到应用程序代码时,处理器就把模式从内核模式改回到用户模式。
进程和线程的关系
1.一个线程只能属于一个进程,但是一个进程可以有多个线程(至少一个线程),一个线程的进程叫做单线程进程,多个线程的进程叫做多线程进程
2.资源分配给进程之后,进程内部的线程都可以共享该进程的资源
3.在处理机上运行的是线程
4.线程在执行的过程中需要协作同步,不同进程的线程需要利用消息通信来实现同步
进程和线程的区别
根本区别: 进程是操作系统分配资源的基本实体,线程是CPU调度的基本单位
开销方面: 每个进程都有自己独立的代码和数据空间,因此进程之间的切换会有较大的开销。但是线程在进程的地址空间内部运行,因此同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,因此线程之间的切换开销小。
所处环境: 在操作系统中能同时运行多个进程,在同一个进程中有多个线程同时执行
内存分配: 系统在运行的时候会给每个进程分配不同的内存空间,但是不会给线程分配,线程使用的资源均来自于进程。进程的地址空间相互独立,同一进程的各线程共享进程的资源,而进程内的线程对其它进程不可见。
包含关系: 线程是进程的一部分,没有线程的进程叫做单线程进程,有多个线程的进程叫做多线程进程
通信方面:进程间通信(IPC)需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接读/写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
应用场景:
对比维度 | 多进程 | 多线程 | 总结 |
数据共享、同步 | 数据共享复杂,需要用IPC;数据是分开的,同步简单 | 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 | 各有优势 |
内存、CPU | 占用内存多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 线程占优 |
创建销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度很快 | 线程占优 |
编程、调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 |
可靠性 | 进程间不会互相影响 | 一个线程挂掉将导致整个进程挂掉 | 进程占优 |
分布式 | 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单 | 适应于多核分布式 | 进程占优 |
1)需要频繁创建销毁的优先用线程
这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的
2)需要进行大量计算的优先使用线程
所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。
这种原则最常见的是图像处理、算法处理。
3)强相关的处理用线程,弱相关的处理用进程
什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
4)可能要扩展到多机分布的用进程,多核分布的用线程
原因请看上面对比。
5)多线程可以充分利用CPU
6)都满足需求的情况下,用你最熟悉、最拿手的方式
Linux进程的特征:
1,在内核里的调度单元是task,线程或者进程没有分别
2,调度策略根据不同版本的内核不一样,有抢占式的,非抢占式的,时间片由调度器根据当前task的状态决定
3,高级别的任务会临时打断当前任务,比如中断,timer,tasklet等
4,task主动出让cpu也会引起调度,比如信号量,锁,sleep。
5,补充一点,进程和线程的主要区别在于map的空间不一样,而不是调度。
简单的概念区分:
进程:
1、进程是磁盘上的可执行文件加载到内存中运行时候的状态,是一种动态的概念,进程是应用程序的运行实例。
2、进程是资源分配的基本单位,以进程为单位向操作系统请求资源:虚拟地址空间、打开文件。
进程的基本状态:就绪、执行、阻塞。
就绪状态:指进程获得了除CPU以外的所有必要资源,只要获得CPU资源便可执行。
执行状态:此时,进程正在执行计算任务,处理指定的操作。
阻塞状态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
进程的状态转换:
(1) 就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
(2) 执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。或者优先级更高的进程占用了CPU资源。
(3) 执行→阻塞
正在执行的进程因等待某种事件(或者说必要的系统资源)发生而无法继续执行时,便从执行状态变成阻塞状态。
(4) 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态
线程:
1、线程是相对于进程来说更小的一个概念:线程只是进程里的一条执行序列,基本上不拥有自己的资源,只拥有属于自己的栈(乌班图是线程栈默认是8M,$ulimit -a 查看)、寄存器、程序计数器,但是它可以与同属于一个进程的其他所有线程共享进程所拥有的全部资源,因为进程和线程的地址空间是相同的。
2、在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度。
线程的基本状态:就绪、阻塞、执行三中状态。其状态转换与进程类似,此处不再阐述。
要重点说明的是线程的阻塞状态:
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
① 等待阻塞:运行的线程执行pthread_cond_wait()方法,系统将其放入等待队列;
② 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用;
③ 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时;当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
注:调用Sleep、join时,不会释放所占用的资源,所以会进入阻塞状态;调用Wait时,会释放所占用的资源,所以会进入等待队列。
线程和进程的区别:
子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程的运行中需要使用计算机的内存资源和CPU3、当进程只有一个的时候,进程也可以看做线程被执行,在LINUX下,线程也叫轻量级的进程和普通进程一样拥有自己的内核栈,只是轻量级进程没有自己的地址空间之类的资源,但是和其他进程共享。
三、资源管理
1. 一个进程在创建时包括以下资源:
① 虚拟存储空间;该存储空间包括代码段、特点的进程数据,调用堆栈、堆栈(用于保存运行时运数中途产生的数据)。
② 分配给该进程的资源的操作系统描述符,诸如文件描述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。
③ 安全特性,诸如进程拥有者和进程的权限集(可以容许的操作)。
④ 处理器状态(内文),诸如暂存器内容、物理存储器寻址等。当进程正在运行时,状态通常存储在暂存器,其他情况在存储器。
2. 一个进程中的线程共享的资源:
① 进程的代码段;
② 打开的文件描述符;
③ 进程的当前目录;
④ 进程用户ID、进程组ID;
⑤ 进程的公有数据(信号量、互斥量等等);
(1、.text
2、.data
3、.bss
4、堆
5、共享库
6、文件)
3. 线程独有的资源有
① 线程ID;
② 寄存器组;
③ 线程的堆栈;
④ 错误返回码;
⑤ 线程的信号屏蔽码;
⑥ 线程的优先级;
多线程
https://www.cnblogs.com/valjeanshaw/p/11469514.html(多核cpu多线程多进程)
引入线程的原因:
1) 一个应用程序中同时存在多个任务,其中的部分活动会随时间的推移而阻塞,而另外一部分则不会,例如,一个文字处理软件,前台部分需要从终端设备获得输入或者将处理完的部分输出,而后台线程则可以实现对文字的处理。故对于CPU密集型进程,该用多线程其性能不一定能得到很大提高,但对于IO密集型进程,其性能可得到很大提高。
2) 线程比进程更轻量级,创建和撤销的代价小,在许多系统中,创建一个线程比一个进程要快10~100倍不等。
3) 在多核CPU中,真正的并行有了可能。即在多线程设计中一部分可用来处理前台任务,一部分可用来处理后台任务,实现真正意义上的并行。
4) 线程间的切换代价要比进程切换的代价小,实现相应响应时间的改善,如把程序中的输入输出与其他处理部分分开。
引入多线程的原因:
1)某个操作可能会陷入长时间等待,等待的线程会进入睡眠状态,无法继续执行。多线程执行可以有效利用等待时间。如等待网络响应可能需要几秒的时间。
2)某个操作(常常是计算)会消耗大量的时间,如果只有一个线程,程序和用户之间的交互会中断。多线程可以让一个线程负责交付,另一个线程负责计算。
3)多CPU或者多核计算机,本身具备同时执行多个线程的能力,故单线程无法完全发挥计算机的计算能力。
4)相对于多进程应用,多线程在数据共享方面效率要高很多。
5)程序逻辑本身就要求并发操作。
结合项目:
dpd的算法会花费大量的时间,所以会有单独的线程去计算,计算完成后会callback(如果没有超时的情况)
多线程:
多线程是对多进程的模拟。前者,多个线程共享同一个地址空间和其他资源,后者共享物理内存、磁盘、IO等其他资源,故线程被称为“轻量级进程”。多线程在CPU系统中运行时,线程轮流运行,犹如多道程序设计,制造线程并行运行的假象。在一个有三个CPU密集型的进程中,实际上每个线程在一个CPU上得到的真实CPU速度的三分之一。不过随着技术的发展,目前主流的CPU都已经直接硬件支持多线程,并允许线程在几个纳秒级内完成切换。后续会对多进程、多线程、以及多核之间的关系进行总结。
线程间不像进程之间那样存在很大的独立性,一个进程的多个线程共享进程内部的很多资源,线程间可以互写对方的堆栈,而不同的进程则无法对其他进程的地址空间进行写操作。因此,在实现多线程编程中,应设计合理的同步通信机制,避免数据冲突的现象发生。图3给出了进程、线程的内容,其中进程的内容是该进程的所有线程共享的。
每个进程中的内容 | 每个线程中的内容 |
地址空间 | 程序计数器 |
全局变量 | 寄存器 |
打开文件 | 堆栈 |
子进程 | 状态 |
即将发生的报警 |
|
信号与信号处理程序 |
|
账户信号 |
|
同步、互斥信号量 |
|
进程和线程的切换
进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。所谓处理器回收,实质上就是把进程存放在处理器的寄存器中的中间数据找个地方存起来,从而把处理器的寄存器腾出来让其他进程使用。那么被中止运行进程的中间数据存在何处好呢?当然这个地方应该是进程的私有堆栈。
让进程来占用处理器,实质上是把某个进程存放在私有堆栈中寄存器的数据(前一次本进程被中止时的中间数据)再恢复到处理器的寄存器中去,并把待运行进程的断点送入处理器的程序指针PC,于是待运行进程就开始被处理器运行了,也就是这个进程已经占有处理器的使用权了。
例如:这就像多个同学要分时使用同一张课桌一样,所谓要收回正在使用课桌同学的课桌使用权,实质上就是让他把属于他的东西拿走;而赋予某个同学课桌使用权,只不过就是让他把他的东西放到课桌上罢了。
在切换时,一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的切换实质上就是被中止运行进程与待运行进程上下文的切换。在进程未占用处理器时,进程的上下文是存储在进程的私有堆栈中的。
与进程切换不同,线程的切换开销则小很多,只需要保存和恢复线程的寄存器内存,栈的切换也是通过寄存器的切换来完成的。可见,线程的切换比进程的切换代价要低很多。
进程和线程在多核cpu,多cpu中的运行关系
概念:
线程是cpu调度和分配的基本单位,进程是操作系统进行资源分配(cpu,内存,硬盘io等)的最小单位.
线程一定是运行在核心上(cpu)的。硬件上的多核多线程核C++的多线程大致相当于逻辑概念和实体概念的区别。
C++的多线程,这是一个逻辑概念,其实就是把一个任务拆成并行处理的多个任务,每个任务都是运行在物理实体上(可以认为是CPU核心,超线程的事情不要可以先去管他)。但是,并不是说你一个线程就要固定占用一个核心,因为程序是有时间序列的。这部分操作系统分时并行部分讲的很详细。大致就是由于某些原因(IO,时序,需要等待其他线程的结果或通知之类),不是所有线程在所有时间都在同时用到CPU核心资源,所以就会有操作空间
C++多线程编程,是你自己把任务拆分成独立运行的不同部分,通过一些技术对这些任务进行同步,使之互相协调运行并充分利用CPU资源的技术。OpenMP是自动并行化一个库,它会自动把任务分散到不同的CPU去执行,相当于自动多线程。
单核cpu:
实现多进程依靠于操作系统的进程调度算法,比如时间片轮转算法,比如有3个正在运行的程序(即三个进程),操作系统会让单核cpu轮流来运行这些进程,然后一个进程只运行2ms,这样看起来就像多个进程同时在运行,从而实现多进程.
多线程其实是最大限度的利用cpu资源.一个拥有两个线程的进程的执行时间可能比一个线程的进程执行两遍的时间还长一点,因为线程的切换也需要时间.即采用多线程可能不会提高程序的运行速度,反而会降低速度,但是对于用户来说,可以减少用户的响应时间.
多核cpu:
什么是多核cpu?多核cpu是一枚处理器中集成多个完整的计算引擎(内核).
多核cpu和单核cpu对于进程来说都是并发,并不是并行.
但是多核cpu每一个核心都可以独立执行一个线程,所以多核cpu可以真正实现多线程的并行.比如四核可以把线程1234分配给核心1234,如果还有线程567就要等待cpu的调度.线程1234属于并行;如果一会核心1停止执行线程1改为执行线程5,那线程15属于并发.
多cpu的运行,对应进程的运行状态;多核cpu的运行,对应线程的运行状态。
操作系统会拆分CPU为一段段时间的运行片,轮流分配给不同的程序。对于多cpu,多个进程可以并行在多个cpu中计算,当然也会存在进程切换;对于单cpu,多个进程在这个单cpu中是并发运行,根据时间片读取上下文+执行程序+保存上下文。同一个进程同一时间段只能在一个cpu中运行,如果进程数小于cpu数,那么未使用的cpu将会空闲。
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
对于多核cpu,进程中的多线程并行执行,执行过程中存在线程切换,线程切换开销较小。对于单核cpu,多线程在单cpu中并发执行,根据时间片切换线程。同一个线程同一时间段只能在一个cpu内核中运行,如果线程数小于cpu内核数,那么将有多余的内核空闲。
总结
1 单CPU中进程只能是并发,多CPU计算机中进程可以并行。
2单CPU单核中线程只能并发,单CPU多核中线程可以并行。
3 无论是并发还是并行,使用者来看,看到的是多进程,多线程。