ITPub博客

首页 > 数据库 > 数据库开发技术 > python 并发编程-协程篇(0)-协程一些概念基础认识

python 并发编程-协程篇(0)-协程一些概念基础认识

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-05-28 17:37:04 0 删除 编辑

协程前置说明

之前我们的几个简单的梳理,都说是再讲关于并发的模式的中的:

  • 多进程并发

    • 进程的创建和销毁代价非常高
    • 需要开辟更多的虚拟空间
    • 多进程之间上下文的切换时间消耗大
    • 需要考虑多进程之间同步问题
    • 1)每个新的任务的处理,都需要创建一个新的进程(当然可以使用进程池)来处理任务。
    • 2)多进程方式主要是利用进程再进行IO等待的过程中,转移给其他其他可调度的进程,提高CPU的使用效率(都是为榨干CPU,别让它太闲)
    • 3)一般适用于密集型CPU任务
    • 4)缺点:
  • 多线程并发

    ❝

    线程库中包含几个系统调用行为有:select(),read(), write(), accept()等

    ❝

    从阻塞态到就绪态的唤醒过程是:调用线程库的回调函数,将原来处于 waiting 状态的线程标记为 runnable.

    • 因共享同一个进程空间,也会涉及同步问题
    • 每一个线程包含有一个内核调用栈(kenerl stack) 和 CPU 寄存器下文的 table
    • 线程之间上下文的切换也需要时间消耗
    • python 多线程无法充分利用CPU多核
    • 4)Python 线程内部是基于非阻塞IO系统调用( non-blocking system call) 的方式,且线程的回调是基于操作系统内核事件的通知机制。(是不是类似多路复用的事件回调机制,有待印证,比较对操作系统理解还是很透传)来处理高并发,避免了进程频繁进行切换的时间开销

    • 5)一般适用于密集型IO任务

    • 6)缺点:

    • 1)每个新的任务的处理,都需要创建一个新的线程(当然可以使用线程池)来处理任务。
    • 2)多个线程共享同一个进程空间
    • 3)每个进程有一个调度器,来调度进程内线程的执行,调度器能感知到进程内线程相关的行为,当线程产生一个【系统调用】的时候会从用户态陷入到内核态的时候,线程不会阻塞进程,但是此时线程会进入到阻塞太,同时切换到其他内部线程继续执行,当系统调用相关的数据准备就绪完成后,处于阻塞态的线程又重新被唤醒从就绪态到运行态继续运行。

目的也都是为了多任务和更高效的执行任务和合理的利用CPU。其实还有并发的另一个模式:就是我们一直再说的

  • 协程

协程一些说辞描述

❝

ps:仅仅是我个人的理解,难免有纰漏,如有不对的地方,欢迎指正

关于协程,通常我们第一时间可能知道的一些概念就是:

  • 1:协程的它是异步的,协程又称为微线程,它是实现多任务的另一种方式,只不过是比线程更小的执行单元。
  • 2:在一些异步web框架里面的话,还有一处异步,需要处处异步的说法。
  • 3:协程它是一种单进程和单线程的模式运行
  • 4:协程的运行依赖于事件循环机制
  • 5:协程的实现方式有:生成器的方式,基于greenlet、基于gevent、基于asyncio的方式等
  • 6:协程是一种‘用户态’的线程,操作系统无法感知到协程的存在
  • 7:系统能感知到只有线程,系统对协程没感觉
  • 8:它是一种轻量级的,用户态的执行单元。
  • 9:协程所有的切换和调度是都发生在用户态
  • 10:协程也拥有自己的寄存器上下文和栈
  • 11:一个协程对象本质是一个函数,函数可以理解为是一个生成器对象
  • 12:一个协程函数的不能像普通函数那样执行调用,需要把协程的对象丢到时间循环中去执行。
  • 13:协程是在单线程内,基于事件循环机制上,协程在遇到IO的时候,协程的自身会让出执行权,且保存当前自身栈上的数据,阻塞完成后,则恢复先前保存的寄存器上下文和栈,继续执行。

异步和同步的补充

❝

ps:仅仅是我个人的理解,难免有纰漏,如有不对的地方,欢迎指正

之前在什么的地方,已经说明关于并发、并行、同步、异步、阻塞、非阻塞的一些区分。

可以的话,可以再回去看看(可能之前的梳理还是存在不严谨的):

使用【IO模型】的方式理解:同步,异步,阻塞和非阻塞

使用python 的【Socket】方式实践五个IO模型的示例

---这里再补充简述一下:

通常:

  • 并行:相对某个时间点来说,运行多个程序运行在多个CPU上(最多和CPU数量一致),参照物是相对于的我们的CPU来说。

  • 并发:相对于某个时间段的范围内来说,肉眼自我可以运行多个任务的方式,但是这些任务其实都是运行再一个CPU上的。同一个时间点有且仅有一个任务能执行(拥有CPU的某个时间片,在任意时刻, 一个 CPU 核心上(processor)只能运行一个进程)

  • 同步(Synchronous):是相对于代码的调用来看,通常是指代码调在用IO操作时,必须等待IO操作完成才能返回的调用方式,(某种意义其实也可以理解是一种阻塞,因为此时线程已进入了wait状态)

  • 异步(Asynchronous):是相对于代码的调用来看,通常是是指代码调用IO操作时,不必需要等待IO操作完成就能可以直接返回的调用方式。(某种意义可以看做是非阻塞)

  • 阻塞(Blocking):主要是现对于我们的CPU对线程的调用的一种说法,是指当调用的函数遇到IO的时候,会进行线程的挂载的操作,此时就需要等待。

  • 非阻塞(Nonblocking):主要是现对于我们的CPU对线程的调用的一种说法,是指当调用的函数遇到IO的时候,不会进行线程的挂载的操作,此时不需要等待。

  • 阻塞和非阻塞:通常是相对于IO的操作来说。

上述的几个说法可能会存在一点点难以理解:

因为有时候我们会认为:

  • 同步==阻塞
  • 异步==非阻塞

但是这种说法,也不是说不对,只是说我们的参照物不一样吧。

协程和linux 下的epoll

  • epoll是一种I/O事件通知机制,是linux 内核实现IO多路复用的一个实现。底层是当内核主动通知哪些I/O时间需要处理,不需要用户线程(select的方式)反复的查询,epoll采用事件回调机制,需处理很多的回调,业务逻辑会分散在不同的事件回调中。
❝

pS:比如tornado早期实现的异步模式,在IO调用的时候,同时传入一个函数,作为返回函数,相关的异步结果回调通知就需要在返回函数中对应处理。这样的情况,我们的业务逻辑代码现在被拆成了两部分,比较零散了!

  • 协程的设计核心目标就是:
    • 于IO 多路复用技术,让整个应用在 IO 上非阻塞,实现高效率
    • 为了拥有同步的简单编程方式,同时又想要拥有异步的性能
    • 通过生成器让分散的 callback 函数代码变成同步代码,需要像使用回调函数那样进行异步结果的回调处理
❝

好文推荐参考:

https://zhuanlan.zhihu.com/p/157265239 https://zhuanlan.zhihu.com/p/252761693

协程和线程的区别

相同:

  • 自己的寄存器上下文和栈

不同:

  • 线程是抢占式多任务并发(需要关心同步问题)

  • 协程是协作式多任务并发(同一个进程内单线程的方式)

  • 线程是一个可以被操作系统感知到一个实体

  • 协程无法被操作系统感知到,是程序员自己的创建出来的实体。

  • 线程的调度(状态切换)是由操作系统来控制,是一种内核态上行为

  • 协程的调度是发生在用户态上的,可以由程序员自己来控制调度,避免了无意义的调度

  • 线程的切换大,涉及到内核态上下文等保存和切换, 线程的切换从系统层面__远不止保存和恢复CPU上下文这些,还会涉及到线程缓存等等。

  • 协程开销小,没有上下文的切换的开销

简要的个人理解图示:

❝

个人其他博客地址

❝

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

  • 1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。
  • 2:关于文章内容,有部分内容参考自互联网整理,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111707