【问题标题】:lua_resume(): What is the meaning of the second argument?lua_resume():第二个参数是什么意思?
【发布时间】:2016-10-05 07:24:26
【问题描述】:

注意:下面有一些问题可以说明我的想法,但我要寻找的唯一答案是标题中实际问题的答案。不是在这里要求“一本书”,也不是对所有这些的逐条回复。

我正在尝试从 C API 启动一个协程,让它屈服,然后再继续(可能在从其他协程执行恢复之后)。这是一个相当简单的用例,但lua_resume() 的文档非常混乱:

int lua_resume (lua_State *L, lua_State *from, int nargs);

在给定线程 L 中启动和恢复协程。

要启动一个协程,你将 main 函数加上 任何论点;然后你调用 lua_resume,其中 nargs 是 论据。当协程挂起或完成其调用时,此调用返回 执行。当它返回时,堆栈包含传递给的所有值 lua_yield,或 body 函数返回的所有值。 lua_resume 返回 LUA_YIELD 如果协程产生,LUA_OK 如果协程完成它 执行没有错误,或错误代码(见 lua_pcall)。

如果出现错误,堆栈不会展开,因此您可以使用调试 API 超过它。错误信息在栈顶。

要恢复一个协程,你从最后一个 lua_yield 中删除所有结果,把 在它的堆栈上只有值作为yield的结果传递,然后 调用 lua_resume。

参数from代表正在恢复L的协程,如果有 没有这样的协程,这个参数可以为NULL。

这里“代表正在恢复L的协程”的含义非常不清楚。究竟什么时候“来自”nil,Lfrom 中的哪一个是“线程栈”,恢复协程的具体要求是什么?状态L 可以在初始lua_resume() 和第二个实际恢复的状态之间修改吗?如果是这样,该州如何知道在哪里/恢复什么功能?如果不是(即每个lua_State 一个线程),创建新线程的正确方法是什么,以便它与父线程共享执行上下文(全局、环境等),以及调用的正确方法是什么lua_resume() 并在这种情况下取​​消每次启动/恢复的调用? 'from' 参数是否与这些事情有关?

最后,在任何一种情况下 - 我如何才能获得完整的堆栈跟踪以在错误处理程序中进行调试(例如,在来自 lua_pcall 的错误时调用)尊重/知道跨简历的调用? lua_getinfo() 是否通过简历正确报告?这就是 'from' 参数的用途吗?

我真的很想要一个完整的、工作的 C API 中的协程启动/恢复示例,它说明了第二个参数的使用。要点上有 this example,但它已经有多年历史了,并且 API 从那时起发生了变化 - lua_resume() 现在需要 3 个参数,而不是 2 个......

【问题讨论】:

  • 你就这类事情问了太多方式的问题,不到一本书的章节就无法回答。
  • 并非如此 - 以上所有问题都可以通过一个简单的、具有三参数格式的 lua_resume() 示例来回答 - 我认为这不会占用一本书的章节。我想回答的问题仍然是“第二个参数的含义是什么”,其他问题只是为了说明为什么这很重要。

标签: lua coroutine c-api


【解决方案1】:

究竟什么时候是"from" nil,L和from中哪个是"线程栈"。

如果您要询问from 参数的含义,请输入it would appear that there isn't much of one。只有在执行恢复的 C 函数本身是从协程中调用时才有效。

L 是您将要恢复的参数推送到的堆栈(以及启动函数,如果它是初始恢复调用)。所以这最适合术语“线程堆栈”。

恢复协程的具体要求是什么?

the docs for lua_status所述,如果线程状态为LUA_OKLUA_YIELD,您可以恢复线程。

请注意,恢复线程和恢复协程是有区别的。 lua_resume 恢复一个线程。如果状态为LUA_OK,则没有活动的协程,因此您正在创建一个 new 协程,因此必须提供一个函数。如果线程是LUA_YIELD,那么恢复线程将恢复产生的协程。

可以在初始 lua_resume() 和第二个实际恢复的状态之间修改状态 L 吗?

当然可以;它必须是。毕竟,您将返回/收益值作为推送到L 的值。当你去恢复它时,你必须删除这些值并将新值作为将从 Lua 的 yield 函数返回的值推送到堆栈中。

所以是的,你可以修改它。但是有一些关于你能做什么的规则。其中两个:

规则#1:你不能戳不属于你的东西。

假设 Lua 堆栈看起来像这样,就在您的初始简历之前(从左到右增长):

[A][B][C][F][1][2][3]

F 是你要启动的函数。 1、2 和 3 是参数。因此,您将 3 传递为 nargsABC 只是您放入该堆栈的任意内容。

如果协程产生,你的堆栈如下所示:

[A][B][C][LOTS OF STUFF][a][b][c]

LOTS OF STUFF 表示由协程处理创建的任意数量的堆栈数据。没有办法知道有多少或有多少。

abc 是 Lua 中传递给 yield 的值。您可以随心所欲地玩它们。您甚至可以通过它们的绝对堆栈索引 1、2 和 3 返回并戳 ABC

不能做的是改变LOTS OF STUFF。该数据代表产生的协程的状态。您不得更改它。完全没有。您可能也无法删除它下面的任何元素。所以你甚至不能删除ABC

规则 #2:您必须准确地从堆栈停止的地方恢复。

协程产生后,您可以从堆栈中删除一些东西并向其中添加东西(根据规则#1)。所以假设堆栈看起来像这样:

[A][B][C][LOTS OF STUFF][a][Q][R][D]

a 是原始返回值之一,其他东西是您在此期间创建的临时值。所以现在是时候恢复协程了。你有一些新的参数要传递,1 和 2。你是怎么做到的?

你的第一步必须是收拾你的烂摊子。您必须将堆栈返回到这一点:

[A][B][C][LOTS OF STUFF]

完成后,您可以推送 1 和 2,然后以 nargs 为 2 恢复协程。

如果是这样,状态如何知道要在哪里/什么功能恢复?

上面的LOTS OF STUFF 部分告诉它“在哪里/恢复什么功能”。

创建新线程的正确方法是什么,以便它与父线程共享执行上下文(全局、环境等)

“执行上下文”与线程无关。这些东西与函数相关,而不是线程。因此,如果您希望一个函数与其他函数共享相同的环境,那么它们应该与该函数共享相同的环境。

这些函数的执行方式(协程与否)与它们的环境无关。

最后,在任何一种情况下 - 我如何才能获得完整的堆栈跟踪以在错误处理程序(例如从 lua_pcall 错误调用)中进行调试,该处理程序尊重/知道跨简历的调用?

你不能。

lua_resume 因错误而失败时,它会在堆栈上留下信息,以跟踪在恢复和发出错误的地方之间发生了什么。但它无法知道简历本身是如何发生的。这是你必须做的事情,这取决于你如何恢复那个协程。

from 字段的用途不是将恢复者连接到被恢复者。

【讨论】:

  • 您似乎在说“lua_resume 将一些值推送到堆栈上,这些值稍后在第二次调用时实际恢复”并回答问题“'from' 应该始终为 NULL,除非lua_resume 正在从协程中调用”。我说的对吗?
  • 如果是这样,那么要执行诸如“工作池”之类的操作,在其中您有多个被选中/恢复/等的协程,您必须能够以某种方式访问​​ [LOTS OF STUFF] - 否则您必须总是以 LIFO 顺序完全完成协程 - 即。它们根本不是真正的协程。如果你说的是真的,你怎么可能交替运行 A 和 B 直到它们一次又一次地屈服?
  • (对此的一个提示可能是您引用的另一个 SO 问题中的“微不足道”示例,它链接到 std lib 源 - 协程 lib 函数肯定是从'from' 堆栈到 'L' 堆栈,反之亦然 - 它们在某种意义上是“连接的”!所以这让我怀疑预期用途有些不同/应该涉及多个 lua_State / 堆栈......)
  • @BadZen:“要执行类似“工作池”之类的操作,在其中您有多个被选中/恢复/等的协程,您必须能够以某种方式访问​​ [LOTS OF STUFF]" 嗯,为什么?如果你想拥有一个“工人池”,只需为每个工人创建一个lua_Statelua_newthread。它们不是昂贵的物品。
  • @user202729:从头开始。我有found some information,这让我对这个答案产生了很大的怀疑。
猜你喜欢
  • 2011-10-22
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-06
相关资源
最近更新 更多