一. 协程的定义

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. Python示例项目学习

    原文地址:http://www.360doc.com/showweb/0/0/874025604.aspx 「 Python3 实现火车票查询工具 」   相信很多人学Python都是冲着它强大的爬虫 ...

  2. 【4opencv】为基于OpenCV的图像处理程序编写界面—关于QT\MFC\CSharp的选择以及GOCW的介绍

            基于OpenCV编写图像处理项目,除了算法以外,比较重要一个问题就是界面设计问题.对于c++语系的程序员来说,一般来说有QT/MFC两种考虑.QT的确功能强大,特别是QML编写andr ...

  3. Android仿微信QQ等实现锁屏消息提醒

    demo代码如下: import android.content.Intent; import android.os.Bundle; import android.support.v7.app.App ...

  4. 使用PhantomJS报warnings.warn('Selenium support for PhantomJS has been deprecated, please use headless '解决方法

    selenium已经放弃PhantomJS了,建议使用火狐或者谷歌无界面浏览器.使用无界面浏览器Selenium+Headless Firefox Selenium+Headless Firefox和 ...

  5. 【转载】 TensorFlow学习——tf.GPUOptions和tf.ConfigProto用法解析

    原文地址: https://blog.csdn.net/c20081052/article/details/82345454 ------------------------------------- ...

  6. python之terminaltables

    from terminaltables import AsciiTable, DoubleTable, SingleTable from colorclass import Color, Window ...

  7. 【SpringBoot】SpringBoot与Thymeleaf模版(六)

    ---恢复内容开始--- 模板引擎的思想 模板是为了将显示与数据分离,模板技术多种多样,但其本质都是将模板文件和数据通过模板引擎生成最终的HTML代码. Thymeleaf介绍 Thymeleaf是适 ...

  8. RunTime总结:

    oc动态性, 运行时将代码转化为runtime的C代码 RunTime运行流程: 生成对应objc_msgSend方法 isa指针查看当前类有没有这个方法, 之后寻找父类, 每个方法SEL(方法选择器 ...

  9. ASP.NET LinqDataSource数据绑定后,遇到[MissingMethodException: 没有为该对象定义无参数的构造函数。]问题。

    问题出现的情形:LinqDataSource数据绑定到DetailsView或GridView均出错,错误如下: “/”应用程序中的服务器错误. 没有为该对象定义无参数的构造函数. 说明: 执行当前 ...

  10. c# 导出excel的三种方式

    第一种:流导出 SaveFileDialog exe = new SaveFileDialog(); exe.Filter = "Execl files (*.xls)|*.xls" ...