1 并发程序设计 2 定义:把一个具体问题求解 设计成 若干个 可同时执行 的程序模块的方法。 3 目的:充分利用cpu的每一个核,以达到最高的处理性能。 4 特性: 5 1.并行性:多个进程在多道程序系统中并发执行或者 在多处理器系统中并行执行,提高计算效率 6 2.共享性:多个进程共享软件资源,输入和计算共享一个输入缓冲区,计算和输出共享一个输出缓冲区 7 3.交往性:多个进程并发执行存在制约 8 9 10 并发就是同时(宏观)应对 (Dealing With)多件事情的能⼒,并⾏是同时(微观)执⾏(Doing)多件事情的 能⼒”。这句话⾮常透彻地阐述了并发和并⾏的区别,在于“应对”和“执 ⾏”。 11 12 解释1:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。 13 14 解释2:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。 15 16 解释3:并行是在一台处理器上“同时”处理多个任务,并发是在多台处理器上同时处理多个任务。如 hadoop 分布式集群。 17 18 19 20 21 线程: 操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 22 特点: 23 线程是独立的,如果在一个线程中发生异常,不会影响其他线程;一个进程中的所有线程共享内存区域。 24 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 25 26 协程: 是一种用户态的轻量级线程,协程的调度完全由用户控制。 27 协程拥有自己的寄存器上下文和栈。协程调用切换时,将寄存器上下文和栈保存到其他地方,再切回来的时候,恢复先前保存的寄存器上下文和栈, 28 直接操作栈则 没有内核切换的开销,可以不加锁的访问全局的变量,所以上下文切换非常快。 29 30 进程: 一个任务事件,比如python中一个py文件的执行就是一个进程。 操作系统就会为每个进程分配一个独立的内存空间 31 32 33 34 多线程带来的问题:因为是共享内存资源,数据被多个线程同时占用,线程竞争数据混乱不安全。 35 解决办法: 36 互斥锁 37 缺点:1.含锁的代码段只能是单线程运行,阻止了多线程的并发执行,效率降低。2.死锁 38 39 40 并发进程之间的制约关系: 41 进程互斥与进程同步 42 进程互斥:并发进程间相互争夺独占性资源而产生的竞争制约关系 43 进程同步:并发进程为协作完成某个任务而产生的协作制约关系。 44 45 46 临界区:进程中的一个程序段 47 48 概念:进程中与访问某个一次只能被一个进程使用的资源(临界资源---互斥共享变量)相关的程序段。 49 形容那种竞争制约关系。 50 如果两个并发进程同时停留在相关的临界区内,就会发生与时间相关的错误。 51 52 因此,如果两个进程的临界区具有相同的临界资源--互斥共享变量,就必须互斥进入。 53 临界区不相关,则无所谓。 54 55 操作系统管理临界区的三个基本要求: 56 1.一个进程不能无休止的呆在临界区 57 2.一个进程不能无休止的等待进入临界区 58 3.一次至多允许一个进程停留在相关的临界区内。 59 60 临界区的嵌套使用: 61 死锁: 62 如果两个进程都使用相同的两个临界区,比如 63 进程1 :先进x临界区,然后再进入y临界区 64 进程2 :先进y临界区,然后再进入x临界区 65 那么当并发运行时,进程1进入x临界区,进程2进入y临界区,然后两个进程都中断,并等待。----此即死锁。 66 67 如何解决: 68 规定程序申请进入低级临界区,再进行申请高一级临界区后,就不再允许继续申请低级临界区。换句话说,两个进程都改成先进x临界区,再进y临界区。 69 70 操作系统实现临界区管理的尝试: 71 锁:一个布尔变量,表示当前是否有进程进入了临界区,有--->True,无--->False 72 操作系统通过开关中断的方式(但是不方便交给用户程序使用)实现实现互斥资源的有效访问。进临界区---关中断。 73 74 中断:进程将 控制权移交 系统内核,由 内核 重新分配控制权 给其他进程。 75 76 77 pv操作与进程互斥: 78 定义一个信号量(操作系统是依赖这玩意实现进程同步与互斥):一种表示 等待进去临界区的进程队列 的数据结构 79 80 pv操作与进程同步: 81 缓冲区:首先看缓冲区有没有数据,没有就要进程挂起,进行等待 。 82 83 用户程序开发方面,如何实现进程同步与互斥:------>管程:对相关并发进程对共享变量的访问进行抽象,提供一个友善的并发程序设计开发环境。进程必须互斥的调用管程中的过程。 84 管程执行中的signal处理问题:一个等待队列,两种管程进程,其中一种优先级高用来再正在运行的管程结束后释放一个等待队列中的管程前,挂起结束的管程。 85 霍尔管程:基于PV操作原语实现 86 87 以上为进程通信的低级方式! 88 89 进程通信: 90 1.直接通信 send(P,信件)把信件发给进程P; receive(Q,信件) 从进程Q接收信件 91 2.进程间通信:通过一个中间信箱进行,信箱有唯一标识符。 92 send(A,信件):传送到信箱A 93 receive(A,信件)从信箱A接收信件 94 95 信箱的数据结构:包括信箱特征(如容量,指针,信件格式等)和信箱体(存放信件) 96 97 98 3.高级通信规约: 99 3.1基于流的进程通信: 100 多个进程使用一个共享的消息缓冲区(称为管道、多路转接器或套接字) 101 一些进程往消息缓冲区写入字符流 102 一些进程从消息缓冲区读出字符流。 103 104 3.2基于RPC/XDR的客户/服务器计算模式的高级通信规约 105 106 阻塞和挂起的区别: 107 挂起是主动的,阻塞是被动的,阻塞其实就是暂停状态/等待状态。 108 阻塞:正在运行的进程由于发生某事件而暂时无法继续执行,放弃处理机制而处于暂停状态。 109 110 111 死锁: 112 定义:一组进程处于死锁状态是指 :每一个进程都在等待被另一个进程占有的、不能抢占的资源。 113 死锁与系统拥有的资源数量有关,与资源分配测了有关,与进程对资源的使用要求以及并发进程的推进顺序有关。 114 115 产生的4个必要条件: 116 1.互斥条件:任一时刻资源仅为一个进程独占 117 2.占有和等待条件:进程请求资源得不到满足,会一直等待 118 3.不剥夺条件:无法从其他进程抢资源 119 4.循环等待条件:存在一个等待链,每个进程分别等待它前一个进程持有的资源。 120 121 解决方法: 122 1.防止: 123 静态资源分配(预分配):进程运行前分配好一起额所需资源。长期占用,资源使用效率低 124 层次分配:好一点,但使用效率也低。 125 2.避免: 126 在分配资源前,先测试系统状态是不是会发生死锁,再决定受否分配资源。 127 银行家思维:本进程分配了资源后,其他进程是不是能够正常归还所有资源。----本质就是一个循环测试解决。 128 129 130 检测和恢复 131 检测方法:等待资源表 和 占有资源表 根据等待占有关系 形成一个矩阵表。 Wallshall的传递闭包算法检测 132 133 解决方法: 134 3.1回退一步,重新运行进程(操作系统的并发程序运行过程中产生死锁是小概率事件) 135 3.2中止卷入死锁的一个进程,再重执行。 136 137 CPU任务可以分为:计算密集型和读写密集型 138 计算密集型:指cpu的占用率很高,比如视频解码,因此比较吃CPU,适合使用多进程并发,来模拟多核心。 139 I/O密集型:cpu占用率很低,大部分时间在等待I/O操作,比如涉及到网络、硬盘软盘相关任务。读写阶段就容易出现线程堵塞,适合多线程编程。 140 141 python的cpython版本由于GIS全局解释锁的存在,每个进程只有一个线程在运行。