协程讲解
1、背景知识介绍
进程是什么呢?
进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。进程拥有代码和打开的文件资源、数据资源、独立的内存空间。
线程拥有自己的栈空间
有人给出了很好的归纳:
对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元。
无论进程还是线程,都是由操作系统所管理的。
Java中线程具有五种状态:
初始化、可运行、运行中、阻塞、销毁
这五种状态的转化关系如下:
但是,线程不同状态之间的转化是谁来实现的呢?是JVM吗?
并不是。JVM需要通过操作系统内核中的TCB(Thread Control Block)模块来改变线程的状态,这一过程需要耗费一定的CPU资源。
进程和线程的痛点
线程之间是如何进行协作的呢?
最经典的例子就是生产者/消费者模式:
若干个生产者线程向队列中写入数据,若干个消费者线程从队列中消费数据。
如何用java语言实现生产者/消费者模式呢?
让我们来看一看代码:
这段代码做了下面几件事:
1.定义了一个生产者类,一个消费者类。
2.生产者类循环100次,向同步队列当中插入数据。
3.消费者循环监听同步队列,当队列有数据时拉取数据。
4.如果队列满了(达到5个元素),生产者阻塞。
5.如果队列空了,消费者阻塞。
上面的代码正确地实现了生产者/消费者模式,但是却并不是一个高性能的实现。为什么性能不高呢?原因如下:
1.涉及到同步锁。
2.涉及到线程阻塞状态和可运行状态之间的切换。
3.涉及到线程上下文的切换。
以上涉及到的任何一点,都是非常耗费性能的操作。
接下来主角登场了
什么是协程
协程,又称微线程,纤程。英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。
这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
既然协程这么好,它到底是怎么来使用的呢?
协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。
所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。
子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
参考https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868328689835ecd883d910145dfa8227b539725e5ed000