lua中并不能随意yield
更新日期:
在调用lua的yield时,我们可能会遇到”attempt to yield across metamethod/C-call boundary”(lua5.2或5.3为”attempt to yield across a C-call boundary”)这个报错,这报错到底是什么意思呢?又是什么原因引起的这个报错呢?
首先lua中的主协程不能被yield,如果你试图yield主协程,就会收到这个报错,当然如果是在lua5.2或者lua5.3的版本中,yield主协程有个单独的报错是”attempt to yield from outside a coroutine”。
当你yield协程时,倒不会像主协程这样必会报错,但如果你从协程中调入到c层,然后又进入lua层,再yield当前协程,这时将会收到这个报错。
举个例子:
|
|
|
|
运行结果:
|
|
流程:
|
|
为什么这种情况下lua会给出这种报错呢?主要是因为在从c函数调回到coroutine中yield时,coroutine当前的堆栈情况会被保存在lua_State中,因此在调用resume时,lua可以恢复yield时的场景,并继续执行下去。但c函数不会因为coroutine的yield被挂起,它会继续执行下去,函数执行完后堆栈就被销毁了,所以无法再次恢复现场。而且因为c函数不会被yield函数挂起,导致c和lua的行为也不一致了,一个被挂起,一个继续执行完,代码逻辑很可能因此出错。
由此可看出其实这个报错并不是一个严重错误,而是一个保障限制。因为我们的c函数在回调到coroutine后并没有要执行的逻辑,比如例子中testc去掉最后的printf,那么lua_pcall就是最后一个要执行的逻辑,所以c函数并不需要恢复现场。在这种情况下其实lua是不需要报出这个错误的,因为整体逻辑不会收到影响,不过lua为了怕使用者犯错,并没有这么做,毕竟他也不知道你后面有没有要执行的逻辑,除非你告诉他。
在lua5.2以上版本加入了新的API(lua_callk, lua_pcallk, lua_yieldk),变相解决了c无法被挂起而继续执行后续代码的问题。这些API通过传入一个后续执行函数,让resume后调用它,也就是说你可以把c要后续执行的逻辑都放在这个函数中传给API,这样resume后就会执行这些逻辑了,但如果调用这些API时没有传这个函数,那么逻辑就和lua5.1的一样了,依旧会报错。当然如果我们的c函数并没有要执行的后续逻辑,又不希望收到这个报错,可以向API中传一个空函数,这样就不会报错了。