系统性能调优必知必会
陶辉
智链达 CTO,前阿里云 P8 高级技术专家
36367 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
系统性能调优必知必会
15
15
1.0x
00:00/00:00
登录|注册

05 | 协程:如何快速地实现高并发服务?

利用用户态代码实现请求切换
降低切换成本和内存占用空间
不需要关注并发细节,提升开发效率
用同步编程方式写业务逻辑
需要与线程结合工作,充分利用CPU资源
协程的载体是线程
需要完整的协程生态,覆盖常见的组件
所有切换必须由用户态代码完成
协程栈大小较小,内存占用空间低
用户态代码切换协程,与内核切换线程的原理相似
利用用户态代码实现请求切换
在异步化基础上,用同步方式写代码
依赖IO多路复用机制和非阻塞方法
通过应用层代码实现请求切换,降低切换成本和内存占用空间
任何一个线程出错会导致整个进程崩溃
共享内存地址空间,解决了多进程模式的问题
内存空间独立,管理成本高,无法简单共享数据
内核切分CPU执行时间为多个时间片,分发给不同进程
未在生产环境中使用协程的原因
协程的优点
运行效率
开发效率
与线程配合
高性能要求
切换原理
协程
异步化编程
多线程模式
多进程模式
思考题
协程的优点
协程的实现原理
高并发服务的实现方式
协程:如何快速地实现高并发服务?

该思维导图由 AI 生成,仅供参考

你好,我是陶辉。
上一讲谈到,零拷贝通过减少上下文切换次数,提升了文件传输的性能。事实上高并发服务也是通过降低切换成本实现的,这一讲我们来看看它是如何做到的。
如果你需要访问多个服务来完成一个请求的处理,比如实现文件上传功能时,首先访问 Redis 缓存,验证用户是否登陆,再接收 HTTP 消息中的 body 并保存在磁盘上,最后把文件路径等信息写入 MySQL 数据库中,你会怎么做?
用阻塞 API 写同步代码最简单,但一个线程同一时间只能处理一个请求,有限的线程数导致无法实现万级别的并发连接,过多的线程切换也抢走了 CPU 的时间,从而降低了每秒能够处理的请求数量。
为了达到高并发,你可能会选择一个异步框架,用非阻塞 API 把业务逻辑打乱到多个回调函数,通过多路复用实现高并发,然而,由于业务代码过度关注并发细节,需要维护很多中间状态,不但 Bug 率会很高,项目的开发速度也上不去,产品及时上线存在风险。
如果想兼顾开发效率,又能保证高并发,协程就是最好的选择。它可以在保持异步化运行机制的同时,用同步方式写代码,这在实现高并发的同时,缩短了开发周期,是高性能服务未来的发展方向。
你会发现,解决高并发问题的技术一直在变化,从多进程、多线程,到异步化、协程,面对不同的场景,它们都在用各自不同的方式解决问题。我们就来看看,高并发的解决方案是怎么演进的,协程到底解决了什么问题,它又该如何应用。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

协程:高并发服务的快速实现 本文深入探讨了协程在实现高并发服务方面的重要性和应用。传统多进程和多线程模式在实现高并发上存在内存消耗过大和上下文切换成本高等问题。异步化编程通过应用层代码实现请求切换,降低了切换成本和内存占用空间,但容易出错且改造工作困难。协程作为一种兼顾开发效率与运行效率的解决方案,能够在异步化基础上实现高并发,同时使用阻塞函数写同步化代码。协程的出现为高性能服务的未来发展指明了方向。 协程融合了多线程与异步化编程的优点,既保证了开发效率,也提升了运行效率。通过用户态代码切换协程,降低了切换请求的成本,使得协程中的业务代码不用关注自己何时被挂起,何时被执行。优秀的协程生态下,常用服务都有对应的协程SDK,方便业务代码使用。与IO多路复用结合的协程框架可以自动挂起、切换协程,进一步提升开发效率。 然而,协程并不是完全与线程无关,线程可以帮助协程充分使用多核CPU的计算力。遇到无法协程化、会导致内核切换的阻塞函数,或者计算太密集从而长时间占用CPU的任务,还是要放在独立的线程中执行,以防止影响所有协程的执行。 总的来说,本文通过对传统多进程、多线程模式和异步化编程的局限性进行分析,引出了协程作为解决方案的重要性,并阐述了协程的优势和应用场景。对于想要了解高并发服务实现方式的读者来说,本文提供了清晰的技术概览和发展趋势,具有很高的实用价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《系统性能调优必知必会》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(31)

  • 最新
  • 精选
  • 忆水寒
    优点: 1、首先协程是比线程更轻量级的对象,在Linux内核来说,线程和进程最终对应的都是task结构。 2、从操作系统的角度来看,线程是在内核态中调度的,而协程是在用户态调度的,所以相对于线程来说,协程切换的成本更低。 3、协程可以认为是一种并发编程技术,性能比较高,可用性也比较高。Java中的Loom 项目的目标就是支持协程,像go语言更是天然支持协程。 在我们项目中没有用到协程,主要还是使用的还是异步回调方式。 主要原因是:大家不知道协程(接收度比较低,觉得没用过可能会遇到很多坑,万一影响产品稳定性怎么办),而且产品里面已经充斥着大量的回调,没法大规模切换了。

    作者回复: 是的,协程还需要生态上各类SDK的完善

    2020-05-08
    5
    24
  • 郭郭
    老师,有没有比较好的C++协程库推荐一下?

    作者回复: 阿里开源的libeasy你可以考虑下哈,非常高效,在阿里的合伙人多隆写的协程库,代码质量很高

    2020-05-08
    5
    16
  • Geek_89bbab
    再补充一些, 1. 一些协程库会使用共享栈,如腾讯的libco。 2. 协程调度和内核调度相比另一个高效的原因,内核是抢占式的调度,协程是非抢占式的、按需调度,所以协程的调度次数远远小于内核的调度次数。

    作者回复: 谢谢Geek_89bbab的分享!!

    2020-05-09
    4
    12
  • 苦行僧
    就记住一句话 协程就是用户态的线程

    作者回复: 是的

    2020-05-14
    8
  • 杉松壁
    协程既然在用户态,是怎么有权限切换CPU寄存器的?

    作者回复: 通过汇编语言直接修改寄存器的值就可以做到,具体你可以看下汇编指令

    2020-06-28
    4
    5
  • 那时刻
    使用过go语言里的协程,通过GMP来完成goroutine的调度,简单来说,通过P来绑定内核线程M于协程G。通过老师的讲解,加深了理解。

    作者回复: ^_^

    2020-05-08
    3
    5
  • Kvicii.Y
    陶老师,协程和线程的区别点在于:线程是CPU通过寄存器进行切换,协程是在用户态中进行切换,除此之外二者还有什么更细微的差别吗?

    作者回复: 线程是由内核实现的,其切换是在内核态进行,因此有内核态与用户态的切换成本,而且内核很难高度定制化,因此它通常考虑的是通用场景,不会为了高并发服务器做过多优化; 协程是由应用代码实现,且主要是用于高并发服务器,耗费内存很小,多个请求间的切换成本也很低。

    2020-06-20
    2
  • 范闲
    没有用过协程。cpp没有协程标准库。另外协程本身也依赖于线程吧。只不过是一个线程可以对应多个些协程。

    作者回复: 可以参考阿里开源的libeasy,是一个很高效的C/C++协程库

    2020-05-18
    2
  • 托尼斯威特
    老师您好,跟您确认几个问题,不知道我理解的对不对。 大家谈高并发,有的时候谈的是高并发连接,有的时候谈的是高并发请求,高并发连接用epoll eventloop就可以了吧? 这篇文章主要是讲高并发请求。本来业务逻辑里需要阻塞线程,而用协程池取代线程池处理请求,可以节约大量的线程。 我疑惑的是,实际应用服务器一般要么是Cpu瓶颈,要么是内存瓶颈,我们的tomcat服务跑30个线程可以处理200QPS,CPU或者内存就接近100%了。这种情况我怎么觉得异步优化和协程优化都没什么用?

    作者回复: 对,CPU、网卡通常是瓶颈,这时应该看看CPU究竟消耗在哪个函数上了,火焰图是个很好的工具。对于IO型应用,内存一般不是瓶颈。

    2021-07-01
    1
  • 木头发芽
    公司所有的微服务都用go写,所以协程一直都在用,通过这节更深入的理解了协程的出现解决的问题及其原理.对GMP模型的理解有了更底层的知识支撑.

    作者回复: 后面我会再写一篇与GO协程相关的加餐

    2020-08-15
    1
收起评论
显示
设置
留言
31
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部