异步实现方式三:协程

时间:2021-09-26 23:31:45

协程的实现依赖于时间片的分隔


协程就是用户态的线程”,Lua协程是非抢占式的多线程,必须手动在不同的协程间切换,且同一时刻只能有一个协程在运行相当于单线程的能力线程确实比协程的性能更好,因为线程能利用多核达到真正的并行计算,


要理解是什么是“用户态的线程”,必然就要先理解什么是“内核态的线程”。 内核态的线程是由操作系统来进行调度的,在切换线程上下文时,要先保存上一个线程的上下文,然后执行下一个线程,当条件满足时,切换回上一个线程,并恢复上下文。 协程也是如此,只不过,用户态的线程不是由操作系统来调度的,而是由程序员来调度的,是在用户态的。

yield这个关键字就是用来产生中断, 并保存当前的上下文的, 比如说程序的一段代码是访问远程服务器,那这个时候CPU就是空闲的,就用yield让出CPU,接着执行下一段的代码,如果下一段代码还是访问除CPU以外的其它资源,还可以调用yield让出CPU. 继续往下执行,这样就可以用同步的方式写异步的代码了.


优点:

1.无需线程上下文切换的开销

2.无需原子操作锁定及同步的开销

3.方便切换控制流,简化编程模型

4.高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理

缺点:

1.无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

2.进行阻塞操作(如IO读写时)会阻塞掉整个程序


协程的应用多在于异步+回调方式:

function readsome()
	io.read();--若不输入则io阻塞
	print("yield before");
	coroutine.yield();
	print("yield after");
end
local co=coroutine.create(readsome);
coroutine.resume(co);
print("contine ");
coroutine.resume(co);

--yield before

--contine?

--yield after


三个状态:suspended(挂起,协同刚创建完成时或者yield之后)、running(运行)、dead(函数走完后的状态,这时候不能再重新resume)。

 

coroutine.create(arg):根据一个函数创建一个协同程序,参数为一个函数


coroutine.resume(co):使协同从挂起变为运行(1)激活coroutine,也就是让协程函数开始运行;(2)唤醒yield,使挂起的协同接着上次的地方继续运行。该函数可以传入参数

 

coroutine.status(co):查看协同状态

 

coroutine.yield():使正在运行的协同挂起,可以传入参数

 

coroutine.running: 返回当前的协程,如果它被主线程调用的话,返回nil



第一次调用resume时候,因为没有对应的yield在等待它,因此所有传递给resume的额外参数都作为协同程序主函数的参数,resume返回值中,第一个值为true 则表示没有错误,而后面所有的值都是yield传入的参数,对应的yield返回的额外值就是对应resume传入的参数。当一个协同程序结束时,它的主函数所返回的值都将作为对应resume的返回值