https://en.wikipedia.org/wiki/Subroutine

In computer programming, a subroutine is a sequence of program instructions that perform a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.

Subprograms may be defined within programs, or separately in libraries that can be used by multiple programs. In different programming languages, a subroutine may be called a procedure, a function, a routine, a method, or a subprogram. The generic term callable unit is sometimes used.[1]

Subroutines are special cases of ... coroutines.

https://en.wikipedia.org/wiki/Coroutine

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 tasksexceptionsevent loopsiteratorsinfinite lists and pipes.

According to Donald KnuthMelvin Conway coined the term coroutine in 1958 when he applied it to construction of an assembly program.[1] The first published explanation of the coroutine appeared later, in 1963.[2]

Comparison with subroutines[edit]

Subroutines are special cases of coroutines.[3] When subroutines are invoked, execution begins at the start, and once a subroutine exits, it is finished; an instance of a subroutine only returns once, and does not hold state between invocations. By contrast, coroutines can exit by calling other coroutines, which may later return to the point where they were invoked in the original coroutine; from the coroutine's point of view, it is not exiting but calling another coroutine.[3] Thus, a coroutine instance holds state, and varies between invocations; there can be multiple instances of a given coroutine at once. The difference between calling another coroutine by means of "yielding" to it and simply calling another routine (which then, also, would return to the original point), is that the relationship between two coroutines which yield to each other is not that of caller-callee, but instead symmetric.

Any subroutine can be translated to a coroutine which does not call yield.[4]

Here is a simple example of how coroutines can be useful. Suppose you have a consumer-producer relationship where one routine creates items and adds them to a queue and another removes items from the queue and uses them. For reasons of efficiency, you want to add and remove several items at once. The code might look like this:

var q := new queue

coroutine produce
loop
while q is not full
create some new items
add the items to q
yield to consume coroutine consume
loop
while q is not empty
remove some items from q
use the items
yield to produce

The queue is then completely filled or emptied before yielding control to the other coroutine using the yield command. The further coroutines calls are starting right after the yield, in the outer coroutine loop.

Although this example is often used as an introduction to multithreading, two threads are not needed for this: the yield statement can be implemented by a jump directly from one routine into the other.

Comparison with threads[edit]

Coroutines are very similar to threads. However, coroutines are cooperatively multitasked, whereas threads are typically preemptively multitasked. This means that coroutines provide concurrency but not parallelism. The advantages of coroutines over threads are that they may be used in a hard-realtime context (switching between coroutines need not involve any system calls or any blocking calls whatsoever), there is no need for synchronisation primitives such as mutexes, semaphores, etc. in order to guard critical sections, and there is no need for support from the operating system.

It is possible to implement coroutines using preemptively-scheduled threads, in a way that will be transparent to the calling code, but some of the advantages (particularly the suitability for hard-realtime operation and relative cheapness of switching between them) will be lost.

Comparison with generators[edit]

Generators, also known as semicoroutines,[5] are a subset of coroutines. Specifically, while both can yield multiple times, suspending their execution and allowing re-entry at multiple entry points, they differ in coroutines' ability to control where execution continues immediately after they yield, while generators cannot, instead transferring control back to the generator's caller.[6] That is, since generators are primarily used to simplify the writing of iterators, the yield statement in a generator does not specify a coroutine to jump to, but rather passes a value back to a parent routine.

However, it is still possible to implement coroutines on top of a generator facility, with the aid of a top-level dispatcher routine (a trampoline, essentially) that passes control explicitly to child generators identified by tokens passed back from the generators:

var q := new queue
generator produce
loop
while q is not full
create some new items
add the items to q
yield consume
generator consume
loop
while q is not empty
remove some items from q
use the items
yield produce
subroutine dispatcher
var d := new dictionary(generatoriterator)
d[produce] := start produce
d[consume] := start consume
var current := produce
loop
current := next d[current]

A number of implementations of coroutines for languages with generator support but no native coroutines (e.g. Python[7] before 2.5) use this or a similar model.

Comparison with mutual recursion[edit]

Further information: Mutual recursion

Using coroutines for state machines or concurrency is similar to using mutual recursion with tail calls, as in both cases the control changes to a different one of a set of routines. However, coroutines are more flexible and generally more efficient. Since coroutines yield rather than return, and then resume execution rather than restarting from the beginning, they are able to hold state, both variables (as in a closure) and execution point, and yields are not limited to being in tail position; mutually recursive subroutines must either use shared variables or pass state as parameters. Further, each mutually recursive call of a subroutine requires a new stack frame (unless tail call elimination is implemented), while passing control between coroutines uses the existing contexts and can be implemented simply by a jump.

Common uses

Coroutines are useful to implement the following:

  • State machines within a single subroutine, where the state is determined by the current entry/exit point of the procedure; this can result in more readable code compared to use of goto, and may also be implemented via mutual recursion with tail calls.
  • Actor model of concurrency, for instance in video games. Each actor has its own procedures (this again logically separates the code), but they voluntarily give up control to central scheduler, which executes them sequentially (this is a form of cooperative multitasking).
  • Generators, and these are useful for streams – particularly input/output – and for generic traversal of data structures.
  • Communicating sequential processes where each sub-process is a coroutine. Channel inputs/outputs and blocking operations yield coroutines and a scheduler unblocks them on completion events. Alternatively, each sub-process may be the parent of the one following it in the data pipeline (or preceding it, in which case the pattern can be expressed as nested generators).
  • Reverse communication, commonly used in mathematical software, wherein a procedure such as a solver, integral evaluator, ... needs the using process to make a computation, such as evaluating an equation or integrand.

最新文章

  1. 解决qt程序的链接阶段出现 undefined reference 错误
  2. Android-ListView类
  3. BootStrap2学习日记18---提示消息
  4. gradle使用小记
  5. sql - sum() 和 count() 函数的区别
  6. 纯js实现积木(div)拖动效果
  7. 【HTTP 2】HTTP/2 协议概述(HTTP/2 Protocol Overview)
  8. FPGA 时序问题
  9. C# 获取字符的Unicode编码
  10. jquery.cxcalendar 插件基本使用
  11. 基于WebGL架构的3D可视化平台—新风系统演示
  12. 多模块后带来的问题解决方法 - OSGI原形(.NET)
  13. 20145215《网络对抗》Exp3 免杀原理与实践
  14. 并发编程---线程---开启方式---进程线程的区别----Thread的其他属性
  15. jQuery封装 写的的确不错 转载
  16. ZooKeeper 集群的安装部署
  17. 【LeetCode】170. Two Sum III – Data structure design
  18. 远程操作与端口转发 SSH原理与运用
  19. zzuli1985(dp/水dfs郑轻比赛)
  20. NodeJS之 Express框架 app.use(express.static)

热门文章

  1. 2021韩顺平图解Linux课程(全面升级)基础篇
  2. JPDA 远程调试方法记录
  3. 使用代码管理工具(git)管理代码的常用指令合集
  4. Js HTML DOM动画
  5. WebApplicationContext
  6. 新建表后,在sqlserver manager中使用显示引用对象无效
  7. LeetCode 124 二叉树中最大路径和
  8. fastjsion反序列化漏洞渗透测试笔记
  9. 剑指offer 面试题10:斐波那契数列
  10. selenium自动化 | 通过获取cookies登录