并发编程
基本概念¶
- 进程:进程是操作系统进行资源分配和调度的基本单位
- 每个进程都有独立的地址空间,一个进程无法直接访问另一个进程的资源和数据
- 进程间相互隔离,提供了良好的安全性。
- 进程间通信(IPC)相对复杂,开销较大。
- 适合大规模、独立的应用程序,需要强隔离或分布在不同机器上。
- 线程:操作系统能够进行运算调度的最小单位,是进程中的实际运作单位。一个进程可以包含多个线程
- 线程间共享进程资源,易于通信和数据共享。
- 相比进程,线程的创建和上下文切换开销更小。
- 适合需要并发执行多个任务的应用,如服务器、GUI应用等。
- 共享进程的地址空间和资源,但每个线程有自己的执行序列、栈空间和程序计数器。
- 协程:用户态的轻量级线程,它的调度完全由用户控制。
- 协程切换开销极小,因为所有的切换都在用户态完成,无需陷入内核态。
- 协程可以用同步的方式编写异步代码,简化了异步编程模型。
- 与线程类似,协程也共享所在进程的资源,但它们更轻量,能创建成千上万的实例。
- 适合 IO 密集型任务、并发编程,如 Web 服务器、网络爬虫等。
-
协程主要用于需要异步执行但是不涉及密集计算的场景,如延时、IO、异步加载等
-
多线程适用于密集型计算或可以并行执行的任务,特别是可能阻塞主线程的任务
-
协程通过在单个线程内部进行任务切换来实现并发,而不是依赖操作系统的线程调度。
- 协程通常运行在一个事件循环内。事件循环负责调度和管理所有的协程,以及处理 IO 事件、定时器事件等。
- 协程非常适合处理异步 IO 操作,因为它们可以在等待 IO 操作完成时挂起,让出 CPU 给其他协程使用,从而提高程序的整体效率。
- 理论上避免了多线程并发中的数据竞争和锁问题
-
线程之间共享哪些资源,不共享那些资源:
- 共享:代码段、数据段、堆内存、信号和信号处理器、进程打开的文件描述符和其他资源
- 不共享:线程栈、线程上下文、线程特定数据
-
进程间通讯方式
- 匿名管道:允许有血缘关系的进程(如父子进程)进行双向通信,但通常用于单向通信。数据流动类似于水流,一端输入,另一端输出。(创建两个管道来实现全双工)
- 父进程创建两个匿名管道,管道 1(fd1[0]和 fd1[1])和管道 2(fd2[0] 和 fd2[1]);
- 父进程 fork 出子进程,于是对于这两个匿名管道,子进程也分别有两个文件描述符指向匿名管道的读写两端;关闭部分端口
- 管道的本质就是内核在内存中开辟了一个缓冲区,这个缓冲区与管道文件相关联,对管道文件的操作,被内核转换成对这块缓冲区的操作。
- 有名管道:与匿名管道类似,但它们在文件系统中有一个名字,可以在任何没有直接关系的进程间进行通信。
- 套接字
- 信号:通知事件发生,不能携带大量数据
- 消息队列:消息队列允许不同进程读写一个队列,实现复杂的通信。每个消息都是一个数据块,包含了消息类型和数据本身,进程可以异步地交换消息。
- 两个队列或一个(约定收发消息的格式)
- 管道连续数据,消息队列是以消息为单位
- 消息队列更加灵活,如优先级消息
- 共享内存
- 信号量
- 匿名管道:允许有血缘关系的进程(如父子进程)进行双向通信,但通常用于单向通信。数据流动类似于水流,一端输入,另一端输出。(创建两个管道来实现全双工)
-
线程间通讯方式
- 共享内存
- 信号量(pv 操作):通过信号量实现线程间同步
- 信号量内部的计数器表示当前可用的资源数量。计数器的初始值通常表示共享资源的总数量。
- wait() 操作p:线程调用
wait()
时,信号量的计数器会减一。如果计数器的值在减一之前就是零,则调用wait()
的线程将被阻塞,直到计数器大于零。这个操作有时也被称为P
操作。 - signal() 操作v:线程完成对共享资源的使用后,调用
signal()
使信号量的计数器加一。如果有线程因为调用wait()
而被阻塞,这时它们中的一个将被唤醒。这个操作有时也被称为V
操作。 - 用途:当信号量的计数器初始化为1时,它可以作为一个互斥锁;信号量也用于限制对一组资源的同时访问。
- 条件变量(
wait()
,notify()
, 和notifyAll()
) - 消息队列
-
线程的状态:
-
新建状态:已经被创建,但是尚未执行,如java中创建了Thread对象,但是没有start
- 就绪状态:已经准备好运行并等待CPU时间片(可运行,正等待操作系统调度)
- 运行状态:线程正在执行,操作系统分配给线程CPU时间片之后,就从就绪状态变成了运行状态
-
阻塞状态:线程因为某些原因放弃CPU使用权,主动进入等待。(等待阻塞wait、同步阻塞等待锁、其他阻塞sleep、io)
-
等待状态:等待其他线程做出特定动作,通常发生在线程间协作
- 超时等待:有最大等待时间,超时后返回
-
终止状态:运行一节结束(执行完成或者出现错误),不能重新启动
-
安卓中进程间通信方式
- intent活动间传递数据(启动的同时)
- binder:一个进程获取另一个进程的引用,如service绑定
- contentprovider:不同应用之间共享数据的标准API
- BroadcastReceivers:接受系统级或应用级广播消息
- 消息队列和Handler
- 共享文件如SharedPreferences
- Socket(网络)