一、什么是协程?

【百度百科】协程与子例程一样,协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。

协程更适合于用来实现彼此熟悉的程序组件,如合作式多任务,迭代器,无限列表和管道。 协程的概念1963年就被提出来了。但直到最近几年才在某些语言(如Lua)中得到广泛应用。现在在面试的时候,有可能会被问到。笔者就有这样的经历。

协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。

一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。

二、各大语言对协程的支持程度

1.Lua从5.0版开始支持协程的概念,极大的扩展了Lua的能力。

Lua的协程通过扩展库coroutine来实现,其中的所有函数如下(具体可以参考Lua的官方manual):

coroutine.create
coroutine.resume
coroutine.running
coroutine.status
coroutine.wrap
coroutine.yield

当前运行的代码可以看作运行在主协程中(就像C程序的main运行在主线程中),通过create可以创建一个协程,resume以运行此协程,直到新协程调用yield程序才能返回到”主协程“中运行。

协程通常是纯软件实现的多任务,与CPU和操作系统通常没有关系,所以没有理论上限。唯一的缺点似乎就是:它不能同时将 CPU 的多个核用上。但对 lua 来说这通常不是问题,因为一个宿主程序里面是可以允许有多个 lua 状态机的,开多个线程或进程,然后每个核开一个 lua 状态机即可。

2.C++通过Boost.Coroutine实现对协程的支持

3.Java不支持

4.Python通过yield关键字实现协程,Python3.5开始使用async def对原生协程的支持

5.Go语言的Goroutine

Goroutine 其实就是协程解决方案的一种演进和实现。

  • 首先,它内置了 Coroutine 机制。因为要用户态的调度,必须有可以让代码片段可以暂停/继续的机制。
  • 其次,它内置了一个调度器,实现了 Coroutine 的多线程并行调度,同时通过对网络等库的封装,对用户屏蔽了调度细节。
  • 最后,提供了 Channel 机制,用于 Goroutine 之间通信,实现 CSP 并发模型(Communicating Sequential Processes)。因为 Go 的 Channel 是通过语法关键词提供的,对用户屏蔽了许多细节。其实 Go 的 Channel 和 Java 中的 SynchronousQueue 是一样的机制,如果有 buffer 其实就是 ArrayBlockQueue。

三、协程的优缺点

优点:

  • 协程更加轻量,创建成本更小,降低了内存消耗 
    协程本身可以做在用户态,每个协程的体积比线程要小得多,因此一个进程可以容纳数量相当可观的协程
  • 协作式的用户态调度器,减少了 CPU 上下文切换的开销,提高了 CPU 缓存命中率 
    协作式调度相比抢占式调度的优势在于上下文切换开销更少、更容易把缓存跑热。和多线程比,线程数量越多,协程的性能优势就越明显。进程 / 线程的切换需要在内核完成,而协程不需要,协程通过用户态栈实现,更加轻量,速度更快。在重 I/O 的程序里有很大的优势。比如爬虫里,开几百个线程会明显拖慢速度,但是开协程不会。

但协程也放弃了原生线程的优先级概念,如果存在一个较长时间的计算任务,由于内核调度器总是优先 IO 任务,使之尽快得到响应,就将影响到 IO 任务的响应延时。假设这个线程中有一个协程是 CPU 密集型的他没有 IO 操作,也就是自己不会主动触发调度器调度的过程,那么就会出现其他协程得不到执行的情况,所以这种情况下需要程序员自己避免。

此外,单线程的协程方案并不能从根本上避免阻塞,比如文件操作、内存缺页,这都属于影响到延时的因素。

  • 减少同步加锁,整体上提高了性能 
    协程方案基于事件循环方案,减少了同步加锁的频率。但若存在竞争,并不能保证临界区,因此该上锁的地方仍需要加上协程锁。
  • 可以按照同步思维写异步代码,即用同步的逻辑,写由协程调度的回调 
    需要注意的是,协程的确可以减少 callback 的使用但是不能完全替换 callback。基于事件驱动的编程里面反而不能发挥协程的作用而用 callback 更适合。

缺点:

  • 在协程执行中不能有阻塞操作,否则整个线程被阻塞(协程是语言级别的,线程,进程属于操作系统级别)
  • 需要特别关注全局变量、对象引用的使用
  • 协程可以处理 IO 密集型程序的效率问题,但是处理 CPU 密集型不是它的长处。 
    假设这个线程中有一个协程是 CPU 密集型的他没有 IO 操作,也就是自己不会主动触发调度器调度的过程,那么就会出现其他协程得不到执行的情况,所以这种情况下需要程序员自己避免

四、协程跟多线程的区别

协程的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

五、协程的适用场景

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

Coroutines非常适合于实现熟悉的程序组件,例如协作任务、异常、事件循环、迭代器、无限列表和管道。

协程的出现,改变了函数只有唯一运行入口的观点,一个较长的过程,尤其是过程中存在需要等待的步骤,也可以用一个函数来编写,等待的时候“跑”到函数外干点别的,然后再回来接着原来的步骤继续运行,如果采用回调的方式,需将这个过程切成一块一块的,块与块之间可以做点别的事情。

协程的出现使我们的编程方式多了一种选择,但协程的编程,尤其是与一些I/O异步操作结合起来用时,难度还是比较大的。

六、总结

Coroutine(协程)是一种用户态的轻量级线程,特点如下:

  1. 轻量级线程。
  2. 非抢占式多任务处理,由协程主动交出控制权。
  3. 编译器/解释器/虚拟机层面的任务。
  4. 多个协程可能在一个或多个线程上运行。

借用Donald Knuth的一句话总结协程的特点:“子程序就是协程的一种特例。”


我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考文章

  1. https://baike.baidu.com/item/%E5%8D%8F%E7%A8%8B/8652240?fr=aladdin
  2. https://www.liaoxuefeng.com/wiki/897692888725344/923057403198272
  3. https://www.jianshu.com/p/49e80a3d4e04
  4. https://blog.csdn.net/weixin_42825585/article/details/87891968
  5. https://blog.csdn.net/guxch/article/details/82803769
  6. https://www.jianshu.com/p/2782f8c49b2a

操作系统(6)-协程相关推荐

  1. 操作系统(7)-进程、线程、协程的区别

    进程 不共享任何状态 调度由操作系统完成 有独立的内存空间(上下文切换的时候需要保存栈.cpu寄存器.虚拟内存.以及打开的相关句柄等信息,开销大) 通讯主要通过信号传递的方式来实现(实现方式有多种,信 ...

  2. 从根上理解高性能、高并发(七):深入操作系统,一文读懂进程、线程、协程

    本文引用了"一文读懂什么是进程.线程.协程"一文的主要内容,感谢原作者的无私分享. 1.系列文章引言 1.1 文章目的 作为即时通讯技术的开发者来说,高性能.高并发相关的技术概念早 ...

  3. 【操作系统】进程、线程、协程和并发、并行

    文章目录 一.并发介绍 1. 进程和线程 (1)进程 (2)线程 (3)进程与线程的区别 (4)任务调度 (5)何时使用多进程,何时使用多线程? 2. 线程和协程 (1)协程 (2)协程和线程的区别 ...

  4. 从根上理解高性能、高并发(五):深入操作系统,理解高并发中的协程

    本文原题"程序员应如何理解高并发中的协程",转载请联系作者. 1.系列文章引言 1.1 文章目的 作为即时通讯技术的开发者来说,高性能.高并发相关的技术概念早就了然与胸,什么线程池 ...

  5. 4.19 python 网络编程和操作系统部分(TCP/UDP/操作系统概念/进程/线程/协程) 学习笔记

    文章目录 1 网络编程概念 1)基本概念 2)应用-最简单的网络通信 2 TCP协议和UDP协议进阶(网络编程) 1)TCP协议和UDP协议基于socket模块实现 2)粘包现象 3)文件上传和下载代 ...

  6. 连接池和协程池为何能提升并发能力?

    你有没有发现,"内存池"和"进程池"都带有"池"字?其实,这两种技术都属于"池化技术".它通常是由系统预先分配一批资源并 ...

  7. 通俗易懂的Go协程的引入及GMP模型简介

    本文根据Golang深入理解GPM模型加之自己的理解整理而来 Go协程的引入及GMP模型 一.协程的由来 1. 单进程操作系统 2. 多线程/多进程操作系统 3. 引入协程 二.golang对协程的处 ...

  8. Go 学习笔记(22)— 并发(01)[进程、线程、协程、并发和并行、goroutine 启动、goroutine 特点,runtime 包函数]

    Go 语言通过编译器运行时( runtime ),从语言上支持了并发的特性. 虽然 Go 程序编译后生成的是本地可执行代码,但是这些可执行代码必须运行在Go 语言的运行时(Runtime )中.Go ...

  9. 进程 线程 协程_进程,线程,协程那些事

    无论我们写出怎样的程序,最后都是由操作系统来运行我们的程序,而操作系统如何管理我们的程序,我们程序的数据如何保存和计算,这些都是操作系统需要处理的事情,我们只要将写好的程序交给操作系统就好. 虽然操作 ...

最新文章

  1. http://alvinalexander.com/java/jwarehouse/hibernat
  2. 【RecyclerView】 七、RecyclerView.ItemDecoration 条目装饰 ( getItemOffsets 边距设置 )
  3. python的列表元素输出
  4. CRC校验算法的解析,暨对网上的CRC详解的补充
  5. javascript indexOf函数
  6. jupyter notebook和python有什么区别_如何在Jupyter Notebook中使用Python虚拟环境?
  7. html5设置data,HTML5自定义data属性
  8. 引入css外部样式表的注意事项
  9. python数据科学手册_数据科学的Python
  10. 安装conntrack-tools
  11. win10无法运行jre java_win10系统无法安装jre的解决方法
  12. Maven 教程:IDEA开发环境中maven 项目配置JDK9,JDK10,JDK11,JDK12..等EA版本的配置方法 系列教程一
  13. 用 Node JS 看糗百段子
  14. stm32毕业设计 单片机智能温控风扇
  15. 数据挖掘--决策树ID3+k-means聚类分析西瓜数据
  16. Webots中创建舵轮模型
  17. android绘制半圆弧线_半圆形进度条Android - 绘制半圆
  18. Java MMdd 日期格式转换问题
  19. 自定义组件使用v-modle
  20. Mac 在指定目录下打开终端的方式

热门文章

  1. JQuery中button提交表单报TypeError: elem[type] is not a function jquery
  2. RHEL 7.0已发布 CentOS 7 即将到来
  3. Linux脚本编写基础
  4. 上一季诺基亚销售下跌28%
  5. 三国演义告诉我们的60个道理
  6. nginx能不能获取到vue项目#后面的内容
  7. windows复制文件到 vmware centos虚拟机问题
  8. JavaFX布局中图片在表格中无法被自适应缩小?
  9. 垃圾回收算法与实现系列-GC 标记-清除算法
  10. 高效实用Kafka-深入理解Kafka启动配置(使用kafka自身内置Zookeeper)