协程是一种比线程更加轻量级的存在,协程完全由程序所控制(在用户态执行),一个线程可以有多个协程。
多线程工作时,若用线程实现两大弊端,一是系统线程会占用非常多的内存空间;二是过多的线程切换回占用大量的系统时间。
协程可以解决以上2个问题,协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。
协程的注意事项
实际上协程并不是什么银弹,协程只有在等待IO的过程中才能重复利用线程,上面我们已经讲过了,线程在等待IO的过程中会陷入阻塞状态,意识到问题没有?
假设协程运行在线程之上,并且协程调用了一个阻塞IO操作,这时候会发生什么?实际上操作系统并不知道协程的存在,它只知道线程,因此在协程调用阻塞IO操作的时候,操作系统会让线程进入阻塞状态,当前的协程和其它绑定在该线程之上的协程都会陷入阻塞而得不到调度,这往往是不能接受的。
因此在协程中不能调用导致线程阻塞的操作。也就是说,协程只有和异步IO结合起来,才能发挥最大的威力。
那么如何处理在协程中调用阻塞IO的操作呢?一般有2种处理方式:
在调用阻塞IO操作的时候,重新启动一个线程去执行这个操作,等执行完成后,协程再去读取结果。这其实和多线程没有太大区别。
对系统的IO进行封装,改成异步调用的方式,这需要大量的工作,最好寄希望于编程语言原生支持。
协程对计算密集型的任务也没有太大的好处,计算密集型的任务本身不需要大量的线程切换,因此协程的作用也十分有限,反而还增加了协程切换的开销。
总结
在有大量IO操作业务的情况下,我们采用协程替换线程,可以到达很好的效果,一是降低了系统内存,二是减少了系统切换开销,因此系统的性能也会提升。
在协程中尽量不要调用阻塞IO的方法,比如打印,读取文件,Socket接口等,除非改为异步调用的方式,并且协程只有在IO密集型的任务中才会发挥作用。
协程只有和异步IO结合起来才能发挥出最大的威力。