简单来说,线程粒度还不够细。举个例子,在网络服务中,调用read函数读取数据,如果socket缓冲区没有数据,当前线程就会阻塞一直到缓冲区可读才行。注意,整个线程会被阻塞,而并发性能自然会受到影响。
如果能把线程更细粒度区分为很多子任务,线程在多个子任务之间交替执行。比如在子任务A里面调用 read 函数,如果socket不可读,那么子任务A阻塞,让出执行权,线程转而去执行其他的子任务。 当可读条件满足后,线程又唤醒子任务A,从上次read阻塞的地方恢复继续执行。
可以看到,线程并没有阻塞,而是转而去执行其他任务。这对并发就进一步提高了。
另外,这里子任务简单来说就是一个函数罢了,要封装这么一个子任务也很简单,把当前函数的栈空间、寄存器状态保存下来即可。
而这个子任务,其实就是协程的概念。由于它只用一些寄存器状态就可以描述,所以其实协程占用的资源非常少,要实现上万的协程是非常容易的。然而如果是上万个线程,操作系统就要骂娘了。
更多关于协程的切换原理,可以参考以下文章: