8-0-0python语法基础-并发编程-python进程,线程,协程,对比,思考

并发的发展历史:为什么会产生多进程,多线程,协程

  • 目的就是为了并发,并发是为了性能,效率,这一点要提高到最核心最基础的认识里面,
  • 举例:比如一个6层楼,要找一个人,如果1个人搜索就要6层依次搜寻,如果6个人就是6倍的效率提高

为什么会有进程?
其实,在早期计算机并没有包含操作系统,这个时候,这个计算机只跑一个程序,这个程序独享计算机的所有资源,这个时候不存在什么并发问题,但是对计算机的资源来说,确实是一种浪费。
早期编程都是基于单进程来进行,随着计算机技术的发展,于是,操作系统出现了,操作系统改变了这种现状,让计算机可以运行多个程序,并且不同的程序占用独立的计算机资源,如内存,CPU等。
主要是能够同时处理多个任务,多个任务还要进行切换,时间片轮转
如果电脑只能运行一个进程,显然是很浪费的,

为什么会有线程?
进程并不是执行任务的最小单元,每一个进程里面有都一个线程,我们叫做主线程,
早期没有线程,一个进程只能干一个任务,如果有多个任务,只能多起进程,进程太多不行的,
进程内部的内存是共享的,所以需要线程,否则还要涉及到进程间的通信,这比较浪费资源
所以线程的出现解决了两个问题:
1,进程内部的通信
2,一个进程可以处理多个任务,

进程、线程、协程三者之间的关系

进程

  • 1,运行中的程序,就是进程,程序是没有生命的实体,运行起来了就有生命了,
    操作系统可以管理进程,进程是操作系统基本的执行单元,

  • 2,每一个进程都有它自己的地址空间,进程之间是不会混的,比如qq不能访问微信的地址空间,
    操作系统替你隔离开了,这也是操作系统引入进程这个概念的原因,

  • 3,进程是资源分配的基本单元,每一个进程都有一个主线程,
    一个进程可以并发多个线程执行不同任务。

线程

  • 线程是cpu调度的基本单元,是操作系统进行程序调度执行的最小单位,所以线程是cpu调度的,
    线程被包含在进程之中,线程必须依托于进程,最终去执行还是通过线程执行

协程

  • 协程是单线程内执行多任务,完全是自己去控制调度,而非受到操作系统管理,
    协程必须依托于线程,
  • 协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。
  • 其实就是通过一个线程实现代码块相互切换执行。
  • 但是,协程来回切换执行的意义何在呢?协程牛逼的地方到底在哪里呢??
  • 计算型的操作,利用协程来回切换执行,没有任何意义,来回切换并保存状态 反倒会降低性能。
  • IO型的操作,利用协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,
  • 那么就会大大节省资源并提供性能,从而实现异步编程(不等待任务结束就可以去执行其他代码)。

进程和线程的对比:

进程拥有一个完整的虚拟地址空间,不依赖于线程而独立存在;
反之,线程是进程的一部分,没有自己的地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。
1,进程之间的内存是独立的,但是一个进程之内的线程是可以共享的,
2,进程之间切换是慢于线程之间的切换的,

关系问题,资源问题,分工问题,效率问题,数据问题,

第一个结论要记住:进程先有的,然后才有线程,线程是进程的一部分,线程是依赖于进程的,没有进程就没有线程,
第二个结论要记住:一个进程里面一定有一个主线程,进程是一个资源分配的单位,真正执行代码的时候还是线程,
第三个结论要记住:进程需要开辟新的资源空间,所以多线程需要很大的资源才可以实现多任务,但是线程只需要很少的资源就可以实现多任务,
第四个结论要记住:进程之间是数据独立的,所以涉及到进程间的通信问题,但是一个进程的线程之间是资源共享的,

比如:
一个网易云音乐,开启了之后就是一个进程,
使用这个网易云音乐既要下载音乐,又要播放音乐,这就是两个线程,

关于线程与进程的关系,还有一个很生动的例子:
把一条公路看作一道进程,那么公路上的各个车道就是进程中的各个线程。这些线程(车道)共享了进程(道路)的公共资源;这些线程(车道)之间可以并发执行(各个车道相对独立),也可以互相同步(交通信号灯)。

线程和协程对比

  • 线程的栈有8MB,而协程栈的大小通常只有几十 KB。而且,内存池也不会为协程预分配内存,它感知不到协程的存在。更低的内存占用空间为高并发提供了保证,毕竟十万并发请求,就意味着10万个协程。

  • 运行效率极高,协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。

  • 同时,在Python中,协程不需要多线程的锁机制,因为只有一个线程,也不存在变量冲突。

  • 协程对于IO密集型任务非常适用,如果是CPU密集型任务,推荐多进程+协程的方式。对于多核CPU,利用多进程+协程的方式,能充分利用CPU,获得极高的性能。

切换效率

协程 > 线程 > 进程

  • 在使用多进程处理任务时并非进程越多越好,因为进程切换会造成性能开销。

进程,线程,协程实现方法

进程 线程 协程 备注
实现库 标准库mutiprocessing 标准库threading 标准库asyncio
start() × 创建子进程/子线程实例并执行该实例的run()方法
run() × 子进程/子线程需要执行的目标任务
join() × 主进程阻塞等待子进程/子线程结束才继续执行
terminate() × × 终止子进程
is_alive() × 判断子进程/子线程是否终止
daemon × 设置子进程/子线程是否随主进程退出而退出
async × × + 关键字配合标准库asyncio 使用
awiat × × + 关键字配合标准库asyncio 使用
同步锁/互斥锁Lock ×
死锁 ×
递归锁RLock ×

进程、线程、协程间的应用场景

进程、线程、协程间的特性决定了它们的应用场景不同:

多进程:
密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
缺陷:多个进程之间通信成本高,切换开销大。

多线程:
密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
缺陷:同一时间片只能运行一个线程(GIL锁导致的),不能做到高并行,但可以做到高并发

协程:
当程序中存在大量不需要CPU的操作时(I/O),适用于协程。
多线程请求返回是无序的,哪个线程有效处理返回就处理哪个线程,协程返回的数据是有序的。
缺陷:单线程执行,处理密集CPU和本地磁盘I/O的时候,性能较低。处理网络I/O性能比较高

总结

并发编程的相关概念:

进程的调度

  • 1,先来先服务,有一个不好的,就是不利于短作业
  • 2,短作业优先算法,但其对长作业不利;不能保证紧迫性作业(进程)被及时处理;作业的长短只是被估算出来的。
  • 3,时间片轮转算法,就是轮流执行,已经很科学了,
  • 4,多级反馈队列算法,有多个队列,有一个新任务来了放入第一个队列,这是优先级加上时间片轮转,第二个任务来了放入下一级,

并发和并行:

  • 进程的并行:这种只有在多核cpu才可以实现,
  • 进程的并发:这是轮流执行,由于速度很快,看起来像是一起执行的,比如一遍听音乐,一遍写代码,

进程的三状态转换图:非常重要

  • 1,进程一开始运行的时候,是就绪的状态,这是第一个状态,就是告诉cpu,我已经准备好可以运行了,进入排队了,
  • 2,时间片轮转,轮到你了之后,你就运行了,这是第二个状态,
  • 3,发生阻塞,这是第三个状态,比如你的程序让你输入内容,input方法, 这时候是阻塞的,你输入完毕了之后,就又畅通了,
  • 这是等待I/O完成,input,sleep,文件的输入和输出,
  • 事件处理之后,你还要进入就绪状态了,
  • 全部处理完了,就结束了,

同步和异步

  • 1,同步,需要等待,需要排队,你什么也不能干,
  • 2,异步,不需要等待,你可以去做其他事情,

阻塞和非阻塞

  • 1,阻塞,就是input,sleep这些,需要等待,这是阻塞,
  • 2,非阻塞,就是跳过这些阻塞,但是程序中不可避免的需要阻塞,因为需要等待内容处理,

同步异步和阻塞非阻塞:

  • 同步阻塞
  • 同步非阻塞
  • 异步阻塞
  • 异步非阻塞,效率更高,
posted @ 2021-11-22 20:01  技术改变命运Andy  阅读(301)  评论(0编辑  收藏  举报