协程有哪些好处?
我一直在学习一些用于游戏开发的Lua。我听说其他语言中有协程(coroutines)的概念,但是在Lua中才真正了解了它们。我并不真正理解它们有多有用,我听说了很多有关它们可以实现多线程事物的讨论,但是它们不是按顺序运行吗?那么,与按顺序运行的普通函数相比,有什么好处呢?对我来说,我还没有完全理解它们与函数之间的不同之处,除了它们可以暂停并让另一个函数运行一秒钟。似乎对我来说用例的场景并不会那么大。
有人能解释一下为什么有人从中受益吗?
尤其是从游戏编程的角度来看,有更深入的见解就好了^^
原文链接 https://stackoverflow.com/questions/7224625
我只是不理解他们与函数的不同之处,除了它们可以暂停并让另一个运行一秒钟。 这是一个非常重要的属性。我曾经在一个游戏引擎上工作,它使用它们来计时。例如,我们有一个引擎,以每秒10个tick的速度运行,你可以等待x个tick的WaitTicks(x),在用户层中,你可以运行WaitFrames(x)等待x帧。即使专业的原生并发库也会使用相同类型的yielding行为。
好的,从游戏开发的角度思考。
假设你正在制作一段切场动画或教程。无论哪种情况,你都有一个有序的命令序列发送到某些实体。一个实体移动到一个位置,和一个人说话,然后走别的地方。以此类推。一些命令必须等待其他命令完成才能开始。
现在回顾一下游戏的工作方式。每一帧,它必须处理AI、碰撞测试、动画、渲染和声音等等。你只能每帧思考一次。那么你如何将这种代码放入其中,你必须等待某些操作完成才能进行下一个操作?
如果你用C++构建了一个系统,你会有一个在AI之前运行的东西。它将有一个要处理的命令序列。其中一些指令是瞬间完成的,比如“告诉实体X去这里”或“在这里生成实体Y”。其他的则必须等待,比如“告诉实体Z去这里,在它到达这里之前不要处理任何其他命令。”命令处理器必须在每一帧调用,它必须理解“实体在位置”等复杂条件。
在Lua中,它看起来像这样:
local entityX = game:GetEntity("entityX");
entityX:GoToLocation(locX);
local entityY = game:SpawnEntity("entityY", locY);
local entityZ = game:GetEntity("entityZ");
entityZ:GoToLocation(locZ);
do
coroutine.yield();
until (entityZ:isAtLocation(locZ));
return;
在C++方面,你需要每帧恢复一次这个脚本,直到完成为止。一旦返回,你就知道切场动画结束了,所以你可以将控制权返回给用户。
看看Lua逻辑多么简单明了。它完全按照它所说的那样工作。它清晰、明显,因此非常难出错。
协同程序的威力在于能够部分完成某个任务,等待条件变为真,然后移动到下一个任务。
大量适用于游戏开发者的好例子。我将给出另一个应用扩展空间的例子。考虑一个应用程序引擎,能够在执行核心功能的 C 语言中运行用户的 Lua 代码。如果用户需要等待引擎达到特定状态(例如等待数据接收),则可以进行以下处理:
- 多线程 C 程序以在单独的线程中运行 Lua,并添加锁定和同步方法,
- 异常 Lua 程序并使用传递给函数的状态尝试重新启动,以跳过任何应该仅运行一次的代码,或者
- 暂停 Lua 程序,并在 C 中达到状态后恢复它。
第三个选项对我来说是最简单的实现方式,避免了在多个平台上处理多线程的需要。这还允许用户的代码运行不受修改,看起来就像他们调用的函数需要很长时间才能完成。
协程在游戏中的使用:
使用简单,但是在多个地方同时使用容易出错。
要小心,不要在太多地方使用协程。 不要使整个 AI 代码依赖于协程。
协程适合在引入之前不存在的状态时进行快速修复。
这正是 Java 所做的。Sleep() 和 Wait() 这两个函数是让您的游戏无法调试的最佳方式。 如果我是您,我会完全避免任何必须使用像协程一样的 Wait() 函数的代码。
OpenGL API 是您应该注意的东西。它从不使用 wait() 函数,而是使用一个干净的状态机,知道每个对象的状态在哪个状态下。 如果您使用协程,你将会产生许多无状态的代码块,这肯定会让调试变得非常困难。
当您制作类似于文本编辑器、银行应用程序、服务器、数据库等应用程序时,协程是很好的(不适用于游戏)。 但是当您制作可以在任何时候发生任何事情的游戏时,协程就不太适合了,您需要有状态。
所以,在我的看来,协程是一种糟糕的编程方式,是写小型无状态代码的借口。
但这只是我的看法。
这更像是一种信仰。有些人相信协程,有些人则不信。使用情况、实现方式和环境的综合效果才是关键。
不要相信基准测试结果,试图证明在多核 CPU 上使用协程比在单线程中使用循环更快:如果协程比循环慢,那就太丢人了!如果这个运行环境后来在所有核心始终处于负载状态的硬件上运行,它就会变得更慢,出现问题...
所以,它本身并没有什么好处。
有时它很方便。但如果你最终得到了大量的协程、出现了状态改变并失去了范围,那你会咒骂协程。但至少这并不是协程框架的错,还是你自己的问题。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
我们在一个我正在工作的项目中使用它们。对我们来说主要的好处是,有些时候由于某些依赖关系,在异步代码中有一些重要的部分需要按顺序运行。如果你使用协程,你可以强制一个进程等待另一个进程完成。它们并不是做到这一点的唯一方法,但它们可能比其他一些方法简单得多。