0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

谈谈协程的那些事儿

jf_uPRfTJDa 来源: 移动Labs 2024-01-26 11:36 次阅读

Labs 导读

随着异步编程的发展以及各种并发框架的普及,协程作为一种异步编程规范在各类语言中地位逐步提高。我们不单单会在自己的程序中使用协程,各类框架如fastapiaiohttp等也都是基于异步以及协程进行实现。那到底什么是协程?协程是怎么发展来的呢?本文将会对这些问题做一个深入浅出的介绍。

作者:李佳斌

单位:中国移动智慧家庭运营中心

本期Labs带大家认识下

协程的那些事儿

Part 01进程,线程到协程

众所周知,计算机操作系统中有两个常见的概念:进程和线程。要讲协程,我们先从这两个基本概念入手。

➢ 进程:操作系统中每一个独立允许的程序,都会占有操作系统分配的资源,是资源分配的基本单位。进程之间互不干涉,都只负责运行自己的指令,这就是进程。

➢ 线程:进程中的一个实体,是被系统独立调度和CPU分派资源的基本单位,线程自己不拥有系统资源,只拥有一些运行时必不可少的资源,如自己的堆栈,程序计数器,寄存器数据等。一个进程可以有多个线程,各个线程共享进程所拥有的全部资源。

➢ 协程:协作的线程,也可以被称作微线程,是一种用户态的线程,协程的调度是由用户主动完成的。代表了一种非抢占式的多任务并发的调度思想:协作式调度,即没有优先级高低的区分。

- 对比

1、从内存占用,上下文切换内容,上下文切换过程等角度进行详细对比。

8dc84184-bb65-11ee-8b88-92fbcf53809c.png

2、从包容关系上来说,一个进程至少包含一个线程,一个线程里面有0个或者多个协程,因此可归纳为如下图:

8dec8972-bb65-11ee-8b88-92fbcf53809c.png

Part 02从异步编程说起

异步编程,也可以叫做并发编程,并发不同于并行:并行是物理上并行,至少要有2个CPU,两个线程同时运行;而并发可以是单核,通过时间调度算法实现多任务调度,给人感觉是同时运行,实际上某一时刻只有一个线程在运行。异步编程能有效避免主线程被阻塞,特别是对于前端来说,如果主线程被阻塞,会导致APP无响应。常见的异步编程有:多线程,回调,Promise,响应式编程以及协程。

- 多线程

以发微博来举例,发布操作可以简单归结为如下三个操作:

1、获取用户签名数据prepareSubmit

2、携带签名数据进行微博发布内容提交请求postSubmit

3、处理请求,响应结果processPost

最开始我们可能只有10个用户,只需要启动10个线程去操作,但是随着用户数增加到1000个,10000个,这个时候如果启动10000个线程,由于每个线程至少会占用4M,10000个线程会占用39G的内存,对服务器的性能要求太高了,并且线程之间的切换也会占用大量的系统时间。因此这种方式只适用于线程之间没有竞争关系,占用内存资源少,切对时延不敏感的情况。

8e0734f2-bb65-11ee-8b88-92fbcf53809c.png

- 回调

如果用异步回调的等方式解决上面发微博的问题,我们可以用如下代码来解决,这种方式简单易懂,使用范围也很广,几乎所有的异步框架都用到了回调。但是也有很明显的问题:

1、如果步骤很多就会出现嵌套地狱

2、对于异常的情况很难处理和传递

3、如果某一个步骤要等多个回调完成之后再进行收口操作,也很困难

8e1c9b4e-bb65-11ee-8b88-92fbcf53809c.png

- Promise

Promise是说对于一个耗时比较久的操作,程序给你一个承诺,保证不久之后会把结果告知你。它采用了链式编程模型,简化了回调的异步操作,解决了嵌套地狱的问题,Promise有以下几种状态:

待定(pending): 初始状态,既没有被兑现,也没有被拒绝。

兑现(fulfilled): 操作成功完成。

拒绝(rejected): 操作失败。

发微博问题使用promise来解决如图,必须等前置条件兑现之后才往后。

8e28f3c6-bb65-11ee-8b88-92fbcf53809c.png

Promise存在如下问题:

1、每一步的返回值类型都必须是 Promise,不能是实际的数据类型

2、错误处理变得复杂,不同阶段产生的错误很难一路传递下去

3、不同阶段之间共享数据困难

- 响应式编程

响应式编程(Reactive Extension简称Rx)的核心是将一切当作数据流,关注数据流的变换和流转,描述数据输入与输出之间的关系,会实现数量众多的扩展函数,这些函数只对输入和输出负责,因此可以很轻松的将函数分发到其他线程上实现异步调用。但Rx调试比较困难,学习成本较高,维护也不易。

- 协程

考虑到大部分互联网请求都是IO密集型而不是CPU密集型,基本的流程都是:请求-少量计算-调用公共服务-大量读写数据库-返回数据。因此IO密集型很容易发生读写阻塞,此时会进行线程切换,执行其他线程。但线程是宝贵的计算资源,因此我们希望线程不要阻塞,一直跑,不要切换上下文。针对这种需求,协程的优势就出来了。协程执行如图:

8e497d26-bb65-11ee-8b88-92fbcf53809c.png

★ 优点

1)协程的创建,销毁和调度都发生在用户态,避免CPU频繁切换带来的资源浪费

2)内存占用小,可以轻松创建几十万的协程

3)可读性高,易维护,代码基本等同于同步

4)通过结构化并发限制控制域,减少内存泄漏

Part 03种类划分

- 按照调用栈分类

协程最关键的步骤就是暂停代码和恢复代码执行,实现方法主要基于栈和状态机&闭包两种。通过区分执行协程的时候是否可以在任意嵌套函数中被挂起,可以分为有栈协程和无栈协程,有栈协程可以被挂起,无栈协程不能被挂起。先看正常的函数栈操作:

8e64ef34-bb65-11ee-8b88-92fbcf53809c.png

有栈协程

协程实现的关键点就是如何保存、恢复和切换上下文,如果将一个函数当作协程,当有栈协程对函数的上下文进行保存,恢复和切换操作时,会对这个函数及其嵌套函数,栈针存储的值,寄存器存储的值进行快照操作,之后只需要对快照做,恢复和切换。

无栈协程

相比于有栈协程,无栈协程在不改变调用栈的情况下采用了类似状态机和闭包的方式来存储暂停点的代码信息。在不改变函数调用栈的情况下,我们也不可能在任意一个嵌套函数中挂起协程,这也是无栈协程的特点,同时由于不需要切换栈帧,无栈协程的性能比有栈协程还要高一点。

- 按照调度方式分类

协程的暂停和恢复涉及到控制权的转移,可以分为非对称协程和对称协程。

非对称协程

非对称协程通过暂停和继续两个指令进行控制权转移,暂停之后控制权就会转移给继续指令所在的协程,因此控制权的转移存在较弱的调用方和被调用方的关系。

对称协程

对称协程只有一个继续指令,各协程之间地位是平等的,继续指令执行之后,控制权就会在多个协程之间流转。

Part 04总结

在高并发、高请求当道的今天,合理利用协程势必会提升我们的系统性能和用户体验。当我们的业务操作或者网络请求面临大量IO时,我们可以考虑采用协程替换线程,能够帮助我们的应用降低系统内存占用,同时也减少了系统切换开销,提升系统性能。然而协程虽然很强大,但是也不要过度使用,协程只有和异步IO结合起来才能发挥出最大的威力。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10868

    浏览量

    211839
  • 操作系统
    +关注

    关注

    37

    文章

    6829

    浏览量

    123338
  • 编程
    +关注

    关注

    88

    文章

    3616

    浏览量

    93750
  • 线程
    +关注

    关注

    0

    文章

    505

    浏览量

    19689

原文标题:关于协程,你了解多少?

文章出处:【微信号:5G通信,微信公众号:5G通信】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    电源选型的那些事儿

    电路教程相关知识的资料,关于电源选型的那些事儿
    发表于 10-10 14:34 0次下载

    Linux的那些事儿之我是Sysfs

    Linux的那些事儿之我是Sysfs
    发表于 10-29 09:28 5次下载
    Linux的<b class='flag-5'>那些</b><b class='flag-5'>事儿</b>之我是Sysfs

    Linux的那些事儿之我是SCSI硬盘

    Linux的那些事儿之我是SCSI硬盘
    发表于 10-29 09:32 19次下载
    Linux的<b class='flag-5'>那些</b><b class='flag-5'>事儿</b>之我是SCSI硬盘

    Linux的那些事儿之我是Hub

    Linux的那些事儿之我是Hub
    发表于 10-29 09:37 7次下载
    Linux的<b class='flag-5'>那些</b><b class='flag-5'>事儿</b>之我是Hub

    Linux的那些事儿之我是Block层

    Linux的那些事儿之我是Block层
    发表于 10-29 09:43 9次下载
    Linux的<b class='flag-5'>那些</b><b class='flag-5'>事儿</b>之我是Block层

    分析电路:谈谈反射的那些事儿资料下载

    电子发烧友网为你提供分析电路:谈谈反射的那些事儿资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文资料、参考设计、用户指南、解决方案等资料,希望可以帮助到广大的电子工程师们。
    发表于 04-03 08:48 5次下载
    分析电路:<b class='flag-5'>谈谈</b>反射的<b class='flag-5'>那些</b><b class='flag-5'>事儿</b>资料下载

    Python后端项目的是什么

    最近公司 Python 后端项目进行重构,整个后端逻辑基本都变更为采用“异步”的方式实现。看着满屏幕经过 async await(在 Python 中的实现)修饰的代码,我顿时
    的头像 发表于 09-23 14:38 1335次阅读

    Python与JavaScript的对比及经验技巧

    前言以前没怎么接触前端,对 JavaScript 的异步操作不了解,现在有了点了解。一查发现 Python 和 JavaScript 的发展史简直就是一毛一样!这里大致做下横向对比和总结,便于
    的头像 发表于 10-20 14:30 1941次阅读

    使用channel控制数量

    goroutine 是轻量级线程,调度由 Go 运行时进行管理的。Go 语言的并发控制主要使用关键字 go 开启 goroutine。Go (Goroutine)之间通过信道(
    的头像 发表于 09-19 15:06 1140次阅读

    详解Linux线程、线程与异步编程、与异步

    不是系统级线程,很多时候被称为“轻量级线程”、“微线程”、“纤(fiber)”等。简单来说可以认为
    的头像 发表于 03-16 15:49 985次阅读

    的概念及的挂起函数介绍

    是一种轻量级的线程,它可以在单个线程中实现并发执行。与线程不同,不需要操作系统的上下文切换,因此可以更高效地使用系统资源。Kotlin
    的头像 发表于 04-19 10:20 893次阅读

    Kotlin实战进阶之筑基篇1

    。 Android 中的每个应用都会运行一个主线程,它主要是用来处理 UI,如果主线程上需要处理的任务太多,应用就感觉被卡主一样影响用户体验,得让那些耗时的任务不阻塞主线程的运行。要做到处理网络请求不会阻塞主线程,一个常用的做法就是使用回调,另一种是使用
    的头像 发表于 05-30 16:24 719次阅读
    Kotlin<b class='flag-5'>协</b><b class='flag-5'>程</b>实战进阶之筑基篇1

    Kotlin实战进阶之筑基篇3

    。 Android 中的每个应用都会运行一个主线程,它主要是用来处理 UI,如果主线程上需要处理的任务太多,应用就感觉被卡主一样影响用户体验,得让那些耗时的任务不阻塞主线程的运行。要做到处理网络请求不会阻塞主线程,一个常用的做法就是使用回调,另一种是使用
    的头像 发表于 05-30 16:26 701次阅读

    FreeRTOS任务与介绍

    是为那些资源很少的 MCU 准备的,其开销很小,但是 FreeRTOS 官方已经不打算再更新了。 任务特性: 1、简单。 2、没
    的头像 发表于 09-28 11:02 999次阅读

    的实现与原理

    前言 这个概念很久了,好多程序员是实现过这个组件的,网上关于的文章,博客,论坛都是汗牛充栋,在知乎,github上面也有很多大牛写了关于
    的头像 发表于 11-10 10:57 444次阅读