多线程程序是如何执行代码的?
进程与线程概念、区别、以及线程间的通信
概念
- 进程是对程序运行时的封装,是操作系统分配资源的基本单位,实现操作系统的并发。
- 线程是进程的子单位,是CPU调度和分派的基本单位,实现进程内部的并发。是OS识别的最小执行与调度单位,每个线程独占一个虚拟处理器,每个线程完成不同的任务,但共相一个地址空间。
区别
- 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(每个线程拥有自己的栈段,来保存所有局部变量和零时变量)
- 进程是资源分配的最小单位,线程是CPU调度的最小单位;
进程间的通信
进程间通信主要包括管道、系统IPC(包括
消息队列、信号量、信号、共享内存
等)、以及套接字socket。
- 管道
匿名和命名两种,用于父子进程通信,命名管道还允许不同进程间的通信。
=>匿名管道:半双工,只允许数据往一个方向流动,只能用于父子进程之间的通信,
命名管道FIFO:可在无关的通道间交换数据 - 系统IPC
2.1 消息队列,
2.2 信号量,它是一个计数器,用来控制多个进程对共享资源的访问,信号量用于进程间的互斥与同步,不储存进程间的通讯数据。
而信号是用于通知接收进程某个时间已经发生。
2.3 共享内存
它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等
特点:
共享内存是最快的一种IPC,因为进程是直接对内存进行存取
因为多个进程可以同时操作,所以需要进行同步
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问
3.套接字SOCKET:
socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。
线程间的通讯方式
临界区
:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
互斥量Synchronized/Lock
:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
信号量Semphare
:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
事件(信号),Wait/Notify
:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作进程间通信的方式:
理解多线程程序是如何执行代码
只有理解多线程程序是如何执行代码的,才能处理如何保证互斥、保证同步和死锁问题,
记住线程就是轻量级进程
一个CPU两个线程,怎么执行
- 每个线程是独占CPU资源进行运算和执行,从两个线程来看,是交替运行。两个线程
并发运行
,独占CPU一小会。 - 两个线程是被操作系统使用 调度算法 进行调度,获取到CPU。例如现在的时间片轮转,之前的先来先服务、短作业优先,优先级优先等等
- 两个线程是抢占式执行的,谁先执行谁后执行是说不清楚的。有可能是主线程后续先执行,也有可能是工作线程先执行。
两个CPU两个线程,怎么执行
- 各自占一个CPU,两个线程同行运行,
并行运行
2个CPU4个线程,怎么执行(接近现实计算机执行情况)
1.CPU少于线程数,操作系统必须使用 调度算法,让每一个线程都获取到CPU资源,得以让线程都能向下推进,执行各自的代码
2.在当前这种情况下,存在并行(多个CPU执行多个线程,同时间运行)也存在并发(一个CPU执行多个线程,交替执行)。
4.对于程序猿而言,针对多线程程序,一定要有 “并行”思维。 这样才能学好多线程
3.现在的操作系统就是这种情况,进程/线程数量远远大于CPU的核数,所以,需要操作系统调度。那么就形成了并行或者并发
线程是如何切换的
线程的结构:
当线程要切换时,线程的task_struct里面的
PC指针会(程序计数器)
保存 这个线程的汇编程序中将要执行的
下一条指令
;
那么恢复的时候又是从哪里恢复的:线程的task_struct中上下文信息中恢复,保CPU存寄存器当中的值。