skip to content
clarence blog

有栈协程&无栈协程

/ 4 min read

有栈协程&无栈协程

早起很多书籍和文章把协程成为subroutine,subroutine就是函数,从单词构成上也能理解它大概的意思,sub-routine。

而后来的coroutine就是可以中断并恢复执行的subroutine。唯一区别就是它强调了可以中断挂起,并且恢复,对应的操作就是yield/resume,也就是说把协程当做一个特殊的函数函数调用。

把协程当做一个特殊的函数调用,就需要像函数一样去切换,函数本身就有调用栈,协程的切换就是通过上下文,上下文本质上就是寄存器。

Golang中的goroutine,js里的async/await。

这里的 有栈和无栈的意思不是指协程在运行时是否需要栈空间,因为对于大多数语言来说,一个函数调用另一个函数总是存在调用栈的。

而是指协程是否可以在其任意嵌套函数中被挂起。

有栈协程,即能通过运行时 上下文实现 挂起,恢复

无栈协程,不能

显然有栈协程可以,无栈协程不可以。

对称协程&非对称协程

对称在这里的意思就是 协程之间是否有 抽象意义上的对称关系,即,协程之间不存在调用依赖的优先级关系,每个之间都是彼此独立,对称

  • 对称协程 Symmetric Coroutine:任何一个协程都是相互独立且平等的,调度权可以在任意协程之间转移。
  • 非对称协程 Asymmetric Coroutine:协程出让调度权的目标只能是它的调用者,即协程之间存在调用和被调用关系。

总结

协程的有栈,无栈的本质区别就是 是否可以在 嵌套函数调中被挂起,恢复。

比如用javascript就不能这么写

async function processArray(array) {
    // 显然这里 forEach 是个嵌套函数
    array.forEach(item => {
        // Uncaught SyntaxError:
        // await is only valid in async function
        const result = await doSomething(item)
        ...
    })
}

但是有栈协程的goroutine就可以这么写:

func processArray(array []int) {
    for i := 0; i < len(array); i++ {
        ch := make(chan int)
        go doSomething(array[i], ch)
        result := <- ch
        ...
    }
}

计算机的任何功能上的提升都是要牺牲一定的性能(额外资源),显然有栈协程功能上更强大,但是没有无栈协程性能高。