协程和Goroutines示例
一. 协程的定义
Coroutines are computer-program components that generalize subroutines for non-preemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing familiar program components such as cooperative tasks, exceptions, event loops, iterators, infinite lists and pipes. --Wikipedia
翻译过来就是:协程是一个在多个入口点允许在某些位置挂起和恢复执行的,可以产生非抢占式任务的子程序的计算机程序组件。
协程的本质是用户态的上下文切换。
二. 子程序
子程序是与协程相近的一个概念,在这里也解释一下,与协程进行对比。
我先打个通俗的比方:
假如小明的一家人每天都要喝水(忽略这句废话),所以小明买了饮水机放在自己的房间里,小明老爸也买了饮水机在自己房间里,小明哥哥也买了一个饮水机在自己房里。那么这三个饮水不仅占地方,还费钱。怎么解决这个问题呢?
因此不如一开始就只买一个饮水机,放在大厅里,供他们三个人使用。这样一来,小明,小明老爸,小明哥哥,他们三共同使用一个饮水机了。
这个比方虽夸张了些,但是逻辑上:三个饮水机占地方又费钱,它同样体现在程序设计里面就是占用空间和重复编码。为了避免这种功能上的重复,这种编码(子程序)可以只放在一个地方,供该程序中的其它模块进行调用。
关于子程序,高德纳的《计算机程序设计艺术》卷一(1.4.1)有更为详细的介绍,我引用一下其中的描述:
WHEN A CERTAIN task is to performed at sereral different places in a program ,it is usually undesirable to repeat the coding in each place. To avoid this situation,the coding (called a subroutine) can be put into one place only,and a few extra instructions can be added to restart the outer program properly after the subroutine is finished.Transfer of control between subroutines and main programs is called subroutine linkage.--Donald Knuth.1
翻译过来就是:当在一个程序的若干个不同的地方,都要执行同一个确定的任务时,通常不希望在每个地方都重复编码,为避免这种重复的情况,这种编码(称为子程序)可以只放在一个地方,而且在这个子程序完成之后,可以附加少量额外的指令,以便适当的重新开始外部的程序。子程序与主程序之间控制的转移,称为子程序的链接。[2]
子程序的优点有如下:
- 节省空间
- 使程序逻辑清晰,便于开发者自己和别人调试。
因此可以理解为子程序是为了被“共用“而实现的程序,不过也有专用的子程序,它们只打算出现一个地方。
三. 协程与子程序的区别
Subrountines are special cases of more general program conmponents,called couroutine. In contrast to the unsymmetric relationship between a main routine and a subroutine,there is complete symmetry betweem coroutines,whichi call on each other--Donald Knuth.[3]
大意是:子程序是协程的特殊情况,主程序和子程序的关系是非对称的,是调用与被调用的关系, 而协程之间是完全的对称, 它们可以相互调用。这句话可能不太好理解,我希望读者能很好理解这句话,图在下面:

(这篇回答原先写在知乎上,由于知乎现在已经不太友好,我注销了账户,图片引用自我原先的知乎图片。)
协程的好处: 主程序和协程之间是完成对称的, 是平等的, 是伪异步的. 当其中主程序挂起或阻塞时, 会切换到协程上执行.
我的观点就是,子程序实际上为了解决重复编码的问题,而协程实际上是为了解决: 当进程在阻塞和挂起时,程序员期望程序更高的执行效率,以协程的方式并发执行。 它们的解决的问题不一样,它们的目的不一样,这是最主要的。
协程在执行时会发生两种可能:
- 如果切换到协程之后,该协程执行结束了, 会直接切换回与之对称的主进程(对称关系,其实主进程的说法已经不合适了).
- 如果该协程也发生挂起, 同样会直接切换回主进程. 假设当主进程再次被挂机时, 又回去执行被挂起的协程.
这是我原先的猜测,这两句话可能不太好理解。下面,我用Golang中的goroutine 的代码,进行验证我这一猜想。
四. Golang中的协程示例
下面的程序示例中 multiplyByTwo 是一个协程, 它的作用是把值乘以2.
当主程序main()挂机时, 会自动切换到协程上执行. 该示例参考: Soham Kamani
代码的执行过程:
1.我定义了一个协程, 它的功能是乘以2.
2.主程序main是最开始被调用的, 当time.sleelp执行时, 这个时候主程序进行了挂起, 协程抓了时机, 开始执行!
3.但是我设计的协程, 它计算结束后也开始了挂机. 这个时候主程序又开始执行了.
4.然后主程序又挂起, 协程继续执行, 这次协程执行完毕
5.主程序执行完毕, 然后退出了.
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("1. start run main")
n := 3
// We want to run a goroutine to multiply n by 2
go multiplyByTwo(n)
time.Sleep(time.Second) // suspending
fmt.Println("4. Hi. Back to main again")
time.Sleep(time.Second) // suspending
fmt.Println("6. done!")
}
func multiplyByTwo(num int) int {
fmt.Println("2. start run go-routine")
result := num * 2
fmt.Println("3. go-routine run result: ", result)
time.Sleep(time.Second) // suspending
fmt.Println("5. back to go-routine again!")
return result
}
执行的输出结果:
Output1:
1. start run main
2. start run go-routine
3. go-routine run result: 6
4. Hi. Back to main again
5. back to go-routine again!
6. done!
上面的结果并不是唯一确定的, 也可以是 1,2,3,5,4,6, 其中4,5的位置有随机性. or Output2:
1. start run main
2. start run go-routine
3. go-routine run result: 6
5. back to go-routine again!
4. Hi. Back to main again
6. done!
协程与主程序是对称的关系, 我们无法得知什么时候协程在执行, 使用time.sleep主动挂起是很好的调用协程的策略.
如果你做大量的基准测试,我猜测对于步骤4和步骤5的随机性,我猜测结果是接近是50%的。
这段代码把主程序和协程之间的相互调用体现的淋漓尽致, 这种对称的关系. 它是用户态发生的。
五. References
- 维基百科, Coroutine - Wikipedia
- Knuth, Donald Ervin (1997). Fundamental Algorithms. The Art of Computer Programming. 1 (3rd ed.). Addison-Wesley. Section 1.4.2: Coroutines, pp. 193–200. ISBN 0-201-89683-4.
- 计算程序设计艺术.第一卷,基本算法:第3版/(美)高德纳(Knuth,D.E)著;苏运霖译。--北京:国防工业出版社。章节1.4.2:协程,pp. 178-184 ISBN 987 -7 -118 -02799 -0
- 代码示例的参考源: An introduction to using and visualizing channels in Go ➡️
协程和Goroutines示例的更多相关文章
- Kotlin协程第一个示例剖析及Kotlin线程使用技巧
Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...
- python中协程的使用示例
例子1 把字符串分割为列表 def line_splitter( delimiter = None ): print( 'ready to split' ) result = None while T ...
- 『GoLang』协程与通道
作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发.程序可以在不同的处理器和计算机上同时执行不同的代码段.Go 语言为构建并发程序的基本代码块是 ...
- Unity C#笔记 协程
什么是协程 协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行. 可能看了这段文字介绍还是有点模糊,其实可以用多线程来比较. 多线程 多线程,顾名思义,多条同时执行的线程. 最初 ...
- unity协程coroutine浅析
转载请标明出处:http://www.cnblogs.com/zblade/ 一.序言 在unity的游戏开发中,对于异步操作,有一个避免不了的操作: 协程,以前一直理解的懵懵懂懂,最近认真充电了一下 ...
- Python3 与 C# 并发编程之~ 协程篇
3.协程篇¶ 去年微信公众号就陆陆续续发布了,我一直以为博客也汇总同步了,这几天有朋友说一直没找到,遂发现,的确是漏了,所以补上一篇 在线预览:https://github.lesschina.c ...
- 转载:PHP 协程实现
转自:https://newt0n.github.io/2017/02/10/PHP-%E5%8D%8F%E7%A8%8B%E5%8E%9F%E7%90%86/ 实现 PHP 协程需要了解的基本内容. ...
- python自动化开发学习 进程, 线程, 协程
python自动化开发学习 进程, 线程, 协程 前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...
- 如何正确的在 Android 上使用协程 ?
前言 你还记得是哪一年的 Google IO 正式宣布 Kotlin 成为 Android 一级开发语言吗?是 Google IO 2017 .如今两年时间过去了,站在一名 Android 开发者的角 ...
随机推荐
- Ubuntu 14.04下超级终端Minicom连接ARM(转)
转自:https://blog.csdn.net/ajianyingxiaoqinghan/article/details/70209765 笔者的工作环境: PC系统:Ubuntu 14.04 LT ...
- 聊聊Dubbo(六):核心源码-Filter链原理
转载:https://www.jianshu.com/p/6dd76ce7338f 0 前言 对于Java WEB应用来说,Spring的Filter可以拦截WEB接口调用,但对于Dubbo接口,Sp ...
- zz 勵志貼,成功是努力加对的方向
5-6年工作经验程序员初进大厂,如何适应工作? 李苦李 李苦李 华为 架构师 318 人赞同了该回答 泻药! 与题主背景非常相似. 本人毕业8年+,普通二本,学历不突出,非计算机专业. 唯一不同的 ...
- Java学习-054-Mybatis IN查询,通过 foreach 获取列表数据
通过如下语句查询商品订单信息: ,,,) 在 Mapper.java 中定义如下接口: List<GoodsOrder> findGoodsOrderByIds(String ids); ...
- “庚武讲堂”(v.gw66.net) 缘起
转载自: https://v.gw66.net/origin/ 我叫“庚武”,一个从业10余年的程序员,其实我更愿意自称软件工程师或软件设计师.转眼间倏忽十年,从最开始用ASP.net 2.0做网站入 ...
- 分布式事务二TCC
分布式事务解决方案之TCC 4.1.什么是TCC事务 TCC是Try.Confirm.Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try.确认Confirm.撤销Cancel ...
- IEEE-754格式标准,float,
float float类型数字在计算机中用4个字节存储.遵循IEEE-754格式标准: 一个浮点数有2部分组成:底数m和指数e 底数部分 使用二进制数来表示此浮点数的实际值指数部分 占用8bit的二进 ...
- LODOP打印图片水平居中
其他居中,查看本博客相关博文:LODOP中打印项水平居中简短问答.图片也属于超文本打印项,因此如果想把图片居中,也需要图片本身内容相对于图片打印项宽度居中,然后再设置打印项居中.如图,同一张图片,都设 ...
- WINGIDE 激活失败
WINGIDE 7.1 激活失败 WINGIDE 7.0 激活成功 1 下载 https://www.7down.com/soft/94270.html 2 安装 3 激活 step 1: st ...
- Python入门学习(1)
静态语言: C/C++.Java 编译器一次性生成目标代码,优化更方便 程序运行速度快 动态语言: Python.JavaScript.PHP 执行程序时需要源代码,维护更灵活 源代码在维护灵活.跨多 ...