当前位置: 代码迷 >> 综合 >> 第十二章 并发编程
  详细解决方案

第十二章 并发编程

热度:69   发布时间:2024-02-06 12:53:33.0

目录

  • 第十二章 并发编程
    • 操作系统
    • 多道技术
    • 进程
    • 线程
    • 协程
    • 并发
    • 进程与线程
    • 锁
    • 池

第十二章 并发编程

操作系统

  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 等待池子中所有代码运行完毕才会往下执行

  相关解决方案