能够先看看我以前写的 《再见 异步回调, 再见 Async Await, 10 万 个 协程 的 时代 来 了》 http://www.javashuo.com/article/p-kkudjbvr-cx.html ,html
要实现 协程 , 须要 语言 本身 实现一个 线程池, 而后 把 协程 放到 线程 里 执行 。编程
协程 切换 就是 把 当前 协程 的 栈顶 和 指令计数器 保存起来 , 在 操做系统 里, 栈顶 和 指令计数器 就是 线程 上下文, 由 操做系统 保存,闭包
对于 协程, 语言 能够 在 堆 里 建立一个 协程 对象, 协程 对象 有 一个 栈顶 字段 和 一个 指令计数器 字段,架构
协程 的 栈顶 和 指令计数器 就能够保存到 栈顶 字段 和 指令计数器 字段 里,并发
到 下次 轮到 这个 协程 执行时, 再 把 栈顶 字段 和 指令计数器 字段 的 值 赋值给 寄存器,异步
这样 就能够 实现 协程 的 切换 了 。async
和 操做系统 相似, 语言 须要 维护 一个 协程 的 就绪队列 和 一个 挂起队列 , 协程 在 等待 其它 协程 或者 线程 时, 须要 挂起, 这样 协程 对象 会 添加到 挂起队列, 当 其它 协程 或 线程 完成时, 会 将 协程 从 挂起 队列 移除, 添加 到 就绪队列, 这样 不久的未来 就能够 继续 执行 协程 。函数
对 就绪队列 和 挂起队列 的 添加 和 移除 操做 是一个 队列 的 操做, 考虑到 并发, 须要在 添加 和 移除 时 Lock(同步 / 互斥), 这可使用 CAS lock , 而不是 操做系统 提供的 lock, CAS 的 性能 更高, new 操做 应该 就是 使用 CAS lock 来 修改 堆 表, 这样的话, 协程 挂起 和 唤醒 的 时间花费 和 new 操做 差很少 。性能
总的来讲, 协程 切换 的 时间花费 至关于 执行了 一段 很短 的 代码 。spa
为何要 语言 本身实现一个 线程池? 而不是 使用 操做系统 提供 的 线程池, 由于 使用 操做系统 提供的 线程池 就成了 async await 了 。 ^^
这又涉及到 协程 和 async await 的 区别 的 问题, 这个问题 和 为何 要 语言 本身 实现一个 线程池 差很少 是 同一个 问题 。
操做系统 的 线程池 的 任务单位 是一个 函数, 因此, async await 使用 操做系统 的 线程池 的 话, 就须要 把 一个 方法 内 跨线程 调用 前 和 后 的 两部分 代码 切割 为 2 个 函数 , 在 语言 的 层面 , 这 2 个 函数 自己是一个 方法(函数), 因此 函数 2 能够 访问 函数 1 中 的 变量, 但 既然 切割 成了 2 个 函数 , 那么 要 让 函数 2 能够 访问 函数 1 中 的 变量, 就须要 闭包 状态机 等 方式 来 共享 这些 变量 。
对于 协程 , 因为 是 本身 实现 的 线程池 , 因此 把 协程 放在 线程 里 执行 不须要 以 函数 为 单位, 而是 载入 协程 的 上下文(栈顶 指令计数器) 到 寄存器, 而后 继续 执行 指令 就能够, 这些是 语言 在 汇编 层面 完成的, 就是说 是 语言 的 编译器 在 生成 目标代码(汇编)时 将 代码 编译 成 这样的 。
因此, 对于 async await 来讲, 把 一个 方法 切割成 函数1 和 函数2 两部分 , 那么 执行 函数1 和 函数2 时 , 二者 有 各自 的 栈顶 , 是 2 次 函数调用,
而 对于 协程, 跨协程 / 跨线程 调用 前 后 的 2 部分 代码 在 执行时 的 栈顶 是 同一个, 是一次 完整的 函数调用 。 只不过 在 跨协程 / 跨线程 调用 时 发生了 “切换” , 即把 寄存器 保存的 栈顶 做为 上下文 保存到 协程 对象 里 , 当 切换回来 时 再 从 协程 对象 的 栈顶 字段 赋值 给 寄存器 , 而后 接着 执行 。
挂起前 和 恢复执行后 的 栈顶 是 同一个, 或者说, 跨协程 / 跨线程 调用 前 后 的 2 部分 代码 的 栈顶 是 同一个, 这 2 部分 代码 是 同一次 函数调用 。
这就是 协程 和 async await 的 本质区别 之一 。
协程 和 async await 的 另外一个 本质区别 是 设计理念 的 不一样 , async await 是把 异步 变得 看起来 像 同步, 而 协程 是 让 切换 轻量化 、平常化 。
因此, 协程 要 实现的, 是 恢复 传统 的 编程 模型, 同步 是 同步 , 异步 是 异步 , 协程 的 数量 没有 限制, 和 对象 同样, 对象 的 数量 能有多少, 协程 的 数量 就能 有多少, 协程 的 切换 至关于 new 一个对象, 这样 就 能 恢复 传统的 正常的 常规的 编程 模型 范式 架构 思惟 思潮 。
这样 就 不须要 异步回调流 为了 节省 线程 就 把 同步 调用 变 异步 , 再 经过 async await 把 异步 代码 变得 “像” 同步代码 。
这 很 糟糕 。
也正由于这样 , 因此 我 说 异步回调流 和 async await 只是一个 过渡, 不可能 长期 霸占 程序界,
并且 , async await 是 怪物 。 哈哈哈哈哈