一. 协程的定义

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]

子程序的优点有如下:

  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]

大意是:子程序是协程的特殊情况,主程序和子程序的关系是非对称的,是调用与被调用的关系, 而协程之间是完全的对称, 它们可以相互调用。这句话可能不太好理解,我希望读者能很好理解这句话,图在下面:

(这篇回答原先写在知乎上,由于知乎现在已经不太友好,我注销了账户,图片引用自我原先的知乎图片。)

协程的好处: 主程序和协程之间是完成对称的, 是平等的, 是伪异步的. 当其中主程序挂起或阻塞时, 会切换到协程上执行.

我的观点就是,子程序实际上为了解决重复编码的问题,而协程实际上是为了解决: 当进程在阻塞和挂起时,程序员期望程序更高的执行效率,以协程的方式并发执行。 它们的解决的问题不一样,它们的目的不一样,这是最主要的。

协程在执行时会发生两种可能:

  1. 如果切换到协程之后,该协程执行结束了, 会直接切换回与之对称的主进程(对称关系,其实主进程的说法已经不合适了).
  2. 如果该协程也发生挂起, 同样会直接切换回主进程. 假设当主进程再次被挂机时, 又回去执行被挂起的协程.

这是我原先的猜测,这两句话可能不太好理解。下面,我用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

  1. 维基百科, Coroutine - Wikipedia
  2. 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. 计算程序设计艺术.第一卷,基本算法:第3版/(美)高德纳(Knuth,D.E)著;苏运霖译。--北京:国防工业出版社。章节1.4.2:协程,pp. 178-184 ISBN 987 -7 -118 -02799 -0
  4. 代码示例的参考源: An introduction to using and visualizing channels in Go ➡️

协程和Goroutines示例的更多相关文章

  1. Kotlin协程第一个示例剖析及Kotlin线程使用技巧

    Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...

  2. python中协程的使用示例

    例子1 把字符串分割为列表 def line_splitter( delimiter = None ): print( 'ready to split' ) result = None while T ...

  3. 『GoLang』协程与通道

    作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发.程序可以在不同的处理器和计算机上同时执行不同的代码段.Go 语言为构建并发程序的基本代码块是 ...

  4. Unity C#笔记 协程

    什么是协程 协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行. 可能看了这段文字介绍还是有点模糊,其实可以用多线程来比较. 多线程 多线程,顾名思义,多条同时执行的线程. 最初 ...

  5. unity协程coroutine浅析

    转载请标明出处:http://www.cnblogs.com/zblade/ 一.序言 在unity的游戏开发中,对于异步操作,有一个避免不了的操作: 协程,以前一直理解的懵懵懂懂,最近认真充电了一下 ...

  6. Python3 与 C# 并发编程之~ 协程篇

      3.协程篇¶ 去年微信公众号就陆陆续续发布了,我一直以为博客也汇总同步了,这几天有朋友说一直没找到,遂发现,的确是漏了,所以补上一篇 在线预览:https://github.lesschina.c ...

  7. 转载:PHP 协程实现

    转自:https://newt0n.github.io/2017/02/10/PHP-%E5%8D%8F%E7%A8%8B%E5%8E%9F%E7%90%86/ 实现 PHP 协程需要了解的基本内容. ...

  8. python自动化开发学习 进程, 线程, 协程

    python自动化开发学习 进程, 线程, 协程   前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...

  9. 如何正确的在 Android 上使用协程 ?

    前言 你还记得是哪一年的 Google IO 正式宣布 Kotlin 成为 Android 一级开发语言吗?是 Google IO 2017 .如今两年时间过去了,站在一名 Android 开发者的角 ...

随机推荐

  1. Keras split train test set when using ImageDataGenerator

    Keras split train test set when using ImageDataGenerator I have a single directory which contains su ...

  2. 多线程高效合作之master-warker模式

    对于高并发的任务,有些任务是相互独立的,任务与任务之间没有依赖关系,因此可以采用 master - worker 模式. master 用于接受任务和分发任务给 worker,并将 worker 返回 ...

  3. Java基础 awt Frame 窗体在屏幕的中间显示

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  4. Mysql 查询一个月当前时间一个月之前的数据

    sql: AND date_format(createDTM, MONTH),'%Y-%m-%d')

  5. 全新思维导图 XMind ZEN v10.0.0 中文破解版

    http://www.carrotchou.blog/20331.html 官网 https://www.xmind.cn/ 注意事项 破解版本已经去除了全部的官方试用版的限制,让大家可以像正版用户一 ...

  6. NLP基本模型

    textcnn: 加载预训练词典:https://blog.csdn.net/nlpuser/article/details/83627709 构建textcnn网络:https://blog.csd ...

  7. c++ extra qualification

    原 c++ extra qualification 2013年01月15日 10:04:52 沈纵情 阅读数 9728   运行代码时候遇到了如下错误: extra qualification ‘Co ...

  8. elementUI vue this.$confirm 和el-dialog 弹出框 移动

    调试了好久, 还能凑合用, 请直接看DOME 示例,复制就能用: <!DOCTYPE html> <html lang="zh"> <head> ...

  9. RHEL 7.6 设置时间同步ntp

    1.服务端和客户端 安装包,检查状态 yum install ntp ntpdate -y systemctl start ntpd systemctl status ntpd 2.服务端 修改配置 ...

  10. Centos7.3之K8S安装初体验

    容器是发展趋势,所以是时候从虚拟机中脱离出来,投入到容器化的怀抱中了. 曾经试过安装k8s,都没有成功,各种乱七八糟的报错,于是一拖再拖,这次总算发现一个可以快速部署的工具,终于安装成功了. 这个k8 ...