协程这个概念, 目前最流行的恐怕就是goroutine了. 有些文章一说到协程, 张口就是绿色线程, 用户态线程, 轻量级线程, 却没解释概念, 也没说实际上是否并行等等其实是取决于具体实现的.
昨晚在知乎看到一篇回答, 有种茅塞顿开的感觉, 我也十分认同他所说的.
我们先说协程的概念上的意思, 协程其实就是一个可主动中断的过程,函数. 在用户空间里实现的, 对内核是不可见的, 运行在线程上. 它也不能直接避免阻塞.
说到阻塞, 我认为一般有io阻塞和计算(cpu)阻塞. 概念上的协程并不能实现非阻塞, 比如你在一个协程调用了一个read方法, 实际上这个read是用syscall调用内核的接口, 会阻塞整个线程, 也就意味着阻塞整个线程上全部的协程.
那么主流语言上的异步, 协程等等是怎么实现非阻塞io的呢, 比如rust的tokio框架, 底层是用mio库, mio是对底层操作系统非阻塞io的抽象, 比如epoll, iocp等等. 而php的amphp框架则是用了php标准库里的非阻塞io函数stream_set_blocking等等.
回到协程的概念, 协程实现非阻塞io, 实际上就是封装了非阻塞io方法, 在io还没有准备好的时候主动去中断, 切换到下一个协程. 直到执行权又一次回到自己, 再检查一次io是否准备好, 如果还没有, 那么继续上面操作.
所以你能看见, 在tokio和amphp这些第三方的异步框架, 都是需要用框架本身提供的封装好的非阻塞api, 而不能直接用std提供的阻塞api.(除非hook了标准库api).
但是你会发现golang是不需要另外区分阻塞和非阻塞api的, 这就跟具体实现有关了.
go我不太了解, goroutine是语言层面实现的, 据其他人所说, goroutine底层是抢占式调度的, 线程池, 所以即使是阻塞io也不会阻塞整个程序. 而且由于是语言层面实现的, 标准库里io api应该对goroutine有特殊优化吧.
而tokio是基于rust的future和async/await来实现的, 有多线程模式, 在多线程上跑多个task, 可以实现任务窃取. 一个task类似一个协程, 但据我所知tokio并不是抢占式调度, 所以在task里非异步的阻塞可能会阻塞整个线程和线程上的全部task.
所以goroutine和tokio多线程模式都是可以 并行 的.
php的amphp是一个纯php实现的异步框架, 同类型的还会有reactphp和用c写的swoole. amphp是用generator(生成器)/yield来实现的异步, 生成器是没有堆栈的, 类似无栈协程, 通过yield来主动中断. 而在php8.1, 以amphp社区为首大力推动的ext-fiber会合并到php-src里, 这意味着php不需要拓展就能支持fiber, 并且amphp v3也使用了fiber.
但是php是单线程的, 所以除了用c实现的swoole以外, 纯php实现的异步框架都是不支持 并行 的.
fiber 又称(纤程), 实际上跟协程是一样的东西不同名字. 那么在php里的fiber跟用generator/yield有什么区别呢?
首先, fiber是有栈的, 并且不会污染整个调用栈. 因为yield这套, 跟rust的async/await一样, 是会传染/污染的, 也就是说, 你可以在异步函数里用同步函数, 但是不能在同步函数里用异步函数, 一旦你返回了一个promise/future, 那么整个调用栈都需要返回一个promise, 直到你扔到执行器为止.(上面那句话可能有点绕, 但是你上手写一下就知道什么意思了).
而php的fiber也是比较轻量的, fiber的栈大小是可以动态增长的, 最开始只有4k, 默认最大8m, 可以手动配置stack size.
随便吐槽一下amphp, amphp的parallel库醒目写着"使用多进程或者线程为php提供真正的并行处理, 无需拓展". 但是实际上你还是得装ext-parallel或者ext-pthreads来支持多线程, 否则纯php只能用多进程来并行. 颇有标题党的味道(
amphp的非阻塞文件io也是得用libuv/libevent等等拓展, 否则回退到同步或者多进程....没错, 多进程😅
假设有甲乙丙在等待调度, 甲运行需要10ms, 乙20ms, 丙1ms.
顾名思义, 协同调度就是需要等待甲10ms, 然后乙20ms, 才轮到甲, 若是甲不小心化了50ms, 不好意思后面等着.
而抢占调度, 则是在一定的时间内, 假设15ms, 甲花了10ms, 轮到乙花了15ms还没跑完的时候, 就会切换到下一个(这里应该有"拿来吧你".jpg).
并且抢占式调度是有优先级的, 简称人上..呸, 高优先级的会比低优先级的更快轮到.