第十二章 并发编程

Posted zhulipeng-1998

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十二章 并发编程相关的知识,希望对你有一定的参考价值。

第十二章 并发编程

操作系统

  1. 控制软硬资源
  2. 调度进度

多道技术

产生背景

针对单核,实现并发

是什么?

内存中存放多道程序

宏观上并行,微观上串行

看起来多个程序同时运行

实际上多道程序轮流使用CPU

时空复用

内存中同时存放多道程序

进程

是什么?

  1. 操作系统进行分配资源和调度的基本单位
  2. 线程的容器
  3. 正在运行的程序过程中的一种抽象
  4. 本质实在多道程序系统中的一次程序执行过程

特征

  1. 动态性:动态创建动态消亡
  2. 并发性:和其他进程并发执行
  3. 独立性:是一个能独立运行的单位
  4. 结构特征:数据、程序、进程控制三跨部分组成

调度算法

  1. 先来先服务:长作业有利,短作业无利

  2. 短作业优先:短作业优先度高,不利于长作业

  3. 时间片轮转法:将CPU处理时间切成时间片,进程用完时间片还没执行完,就排到末尾等待下一次执行

  4. 多级反馈队列:设置多个队列,赋予优先级

    4.1 新进程进入内存,放入第一队列,按照先来先服务调度

    4.2 不能在分配的时间片内完成,降入第二队列,按照先来先服务调度

    依次降级时,按时间片轮转法运行

  5. 进程调度:按照多级反馈队列+时间片轮转调度

并发与并行

  1. 并发:看起来像同时运行

  2. 并行:真正意义上的同时运行

    单核计算机不存在并行,但可以并发

进程的三种状态

  1. 就绪态:进程分配到除CPU外的所有资源
  2. 运行态:进程获得处理机,执行完一个时间片,进入就绪态
  3. 阻塞态:等待时间发生,放弃处理机。(遇IO操作、或使用CPU时间过长)

同步、异步、阻塞、非阻塞

  1. 同步:两个任务等待提交,第二个必须等待第一个提交完毕才能提交
  2. 异步:两个任务等待提交,提交第一个的同时就可以直接提交第二个任务,不用等待。
  3. 阻塞:程序的阻塞态(遇IO操作或CPU执行时间长)
  4. 非阻塞:程序进入就绪态、运行态

进程的两种创建方式

  1. 直接使用process实例
  2. 继承process,重写run方法
进程的使用
from multiprocessing import Process
p=Process(target=func,)
p.start()
p1.join
p.daemon=True

僵尸进程、孤儿进程

  1. 僵尸进程:

    1.1 子进程死亡,父进程没回收

    1.2 任何进程都会变成僵尸进程

  2. 孤儿进程:父进程意外死亡

守护进程

  1. p.daemon=True,在start()之前使用
  2. 在主进程结束后结束

join

等待子进程结束再往下进行

互斥锁

acquire()枪锁

release()释放锁

进程间通信

  1. 多个进程修改同一个数据时,用锁

    1.1 牺牲速度,保证数据安全

    1.2 可能造成死锁,效率低

  2. IPC通信机制:队列+管道

    2.1进程队列

    Queue

    JoinableQueue

    get、put、full、empty、get_nowait

    2.2生产者消费者模型

    生产者: 生产/制造数据

    消费者: 消费/处理数据

    解决了什么问题: 供需不平衡

    怎么解决:

    通过一个阻塞队列,生数据,取数据都通过这个容器

    使用队列Queue

    使用JoinableQueue

    task_done

    join

进程池

是什么
  1. 定义一个池子,里面放上多个进程,有任务来,用来进程池中的进程处理任务.处理完毕,进程重新放入池中。
  2. 池中的进程都被用完了,还有任务来,就等待,直到有进程执行完毕重新回到池子,然后使用进程处理任务
  3. 池中数量固定,都在用就要等待进程空闲。
  4. 降低操作系统调度难度,减少创建开销、时间。
怎么用
  1. from multiprocess import pol

  2. p=Pool(3) 创建三个进程

  3. 同步调用:res=p.apply(work,args=(i,))

  4. 异步调用:

    1. poll.map(func,range(100))

    poll.map(work,[(1,2),‘alex‘]) :map自带join

    (带可迭代参数,异步调用进程,开启100个任务,map自带join的功能)

    1. res=p.apply_async(work,args=(i, ))

    (带其他参数)

为什么要用?

开启和销毁进程,开销大

线程

是什么

  1. 进程想要执行任务需要依赖线程,每个进程至少有一个线程
  2. 执行单位,CPU最小调度单位

为什么要有线程

  1. 开进程,申请内存空间,耗资源
  2. 开线程消耗低于开进程

两种创建方式

  1. from threading import Thread

    继承Thread,重写run 方法

  2. t=Thread(targs=sayhi,args=(‘太白‘,))

多线程

  1. 一个进程内存在多个线程

  2. 多线程无法利用多核优势,为什么要有多线程

    计算密集型

    单核:使用多线程

    多核:使用多进程

    IO密集型

    单核:使用多线程

    多核:使用多线程

全局解释器GIL

  1. 多个线程共享同一份数据不安全
  2. GIL锁保证任意时刻只有一个线程在解释器中运行
  3. 只有Cpython解释器有GIL,本质是一把锁
  4. 多个线程想要执行必须先抢到GIL锁,抢到之后才能运行,释放锁后,其他线程继续抢

event事件

当线程需要通过判断另一个线程的状态才能执行(下一步的时候使用)

协程

进程、线程、协程

进程:资源单位

线程:执行单位

协程:单线程实现并发(程序员YY出来的概念)

用法

  1. 遇到IO操作时,程序员自己检测IO并用代码切换,欺骗操作系统,让他误以为你没有IO这个操作

  2. 使用gevent模块

    monkey.path_all()需要手动配置

    g=spawn(任务名):能够识别IO

    g.join()等待任务结束

并发

本质:切换+保存状态,开起来像同时运行

效率:

  1. 计算密集型:降低效率
  2. IO密集型:提高效率

进程与线程

进程
  1. 资源单位,资源分配最小单位
  2. 切换开销大
  3. 开进程,申请内存空间耗费资源
  4. 执行单位,CPU调度最小单位
线程
  1. 切换开销小
  2. 开线程:多个线程共性资源,开销远低于开进程
守护(进程)线程

等待主进程(主线程)运行完毕,才能被销毁

守护进程:运行完毕指的是主进程运行完毕

守护线程:运行完毕指的是主进程所在的进程内,所有其他非守护线程运行完毕

目的:保护数据安全,当多个进程或多个线程需要修改同一份数据时要用到锁

全局解释器锁GIL

限制任意时刻只有一个线程执行,防止多个线程修改同一份数据

join和互斥锁

  1. join锁住整个代码
  2. 锁只锁住修改共享数据的代码

死锁

  1. 多个进程或多个线程因争夺资源造成互相等待的情况
  2. 为了解决死锁:使用递归锁RLOCK替代LOCK
  3. RLOCK维护一个lock和counter变量,直到一个线程的所有acquire都被释放,其他线程才能获得资源

信号量和锁

  1. 信号量是公共厕所,同时可以进出多个
  2. 锁是自己马桶,只能进去一个,要想进去,必须等上一个人用完

用法:

  1. from concurrent.futures import 1/2

  2. 线程池:pool=ThreadPoolExecute(5),参数不传默认CPU个数乘以5

  3. 提交任务

    同步:两个任务提交,必须第一个完成后第二个才能开始

    异步:两个任务提交,必须等待第一个完成才能开始第二个

    异步回调:pool.submit(任务,任务参数).add_done_callback(callback回调函数)

  4. pool.shutdown 等待池子中所有代码运行完毕才会往下执行

以上是关于第十二章 并发编程的主要内容,如果未能解决你的问题,请参考以下文章

深入理解计算机系统 第十二章 并发编程

第十二章:并发程序的测试——Java并发编程实战

java并发编程实战:第十二章---并发程序的测试

深入理解计算机系统 第十二章 并发编程

深入理解计算机系统 第十二章 并发编程 part1 第二遍

第十二章:面向对象编程