协程有什么好处?
我一直在学习一些用于游戏开发的lua。我听说过其他语言中的协程,但真正接触到它们是在 lua 中。我只是不太明白它们有多有用,我听到很多人谈论它如何成为一种执行多线程操作的方法,但它们不是按顺序运行的吗?那么按顺序运行的正常函数会有什么好处呢?我只是不明白它们与函数有什么不同,除了它们可以暂停并让另一个函数运行一秒钟。似乎用例场景对我来说不会那么大。
有人愿意解释一下为什么有人会从中受益吗?
特别是从游戏编程角度的洞察力会很好^^
I've been learning some lua for game development. I heard about coroutines in other languages but really came up on them in lua. I just don't really understand how useful they are, I heard a lot of talk how it can be a way to do multi-threaded things but aren't they run in order? So what benefit would there be from normal functions that also run in order? I'm just not getting how different they are from functions except that they can pause and let another run for a second. Seems like the use case scenarios wouldn't be that huge to me.
Anyone care to shed some light as to why someone would benefit from them?
Especially insight from a game programming perspective would be nice^^
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
好吧,从游戏开发的角度来思考。
假设您正在制作过场动画或教程。无论哪种方式,您所拥有的都是发送到一定数量的实体的有序命令序列。一个实体移动到一个位置,与一个人交谈,然后走到其他地方。等等。有些命令只有在其他命令完成后才能启动。
现在回顾一下你的游戏是如何运作的。每一帧,它都必须处理人工智能、碰撞测试、动画、渲染和声音,以及其他可能的东西。你只能思考每一帧。那么,如何将这种代码放入必须等待某个操作完成才能执行下一个操作的地方呢?
如果你用 C++ 构建一个系统,你所拥有的将会是在人工智能之前运行的东西。它将有一系列命令要处理。其中一些命令是即时的,例如“告诉实体 X 到这里”或“在此处生成实体 Y”。其他人则必须等待,例如“告诉实体 Z 到这里,并且在它到达这里之前不要处理任何命令。”命令处理器必须在每一帧被调用,并且它必须理解复杂的条件,例如“实体位于位置”等等。
在 Lua 中,它看起来像这样:
在 C++ 大小上,您将每帧恢复此脚本一次,直到完成。一旦它返回,您就知道过场动画已经结束,因此您可以将控制权返回给用户。
看看 Lua 逻辑多么简单。它完全按照它所说的那样做。它很清楚、显而易见,因此很难出错。
协程的强大之处在于能够部分完成某些任务,等待条件成立,然后继续执行下一个任务。
OK, think in terms of game development.
Let's say you're doing a cutscene or perhaps a tutorial. Either way, what you have are an ordered sequence of commands sent to some number of entities. An entity moves to a location, talks to a guy, then walks elsewhere. And so forth. Some commands cannot start until others have finished.
Now look back at how your game works. Every frame, it must process AI, collision tests, animation, rendering, and sound, among possibly other things. You can only think every frame. So how do you put this kind of code in, where you have to wait for some action to complete before doing the next one?
If you built a system in C++, what you would have is something that ran before the AI. It would have a sequence of commands to process. Some of those commands would be instantaneous, like "tell entity X to go here" or "spawn entity Y here." Others would have to wait, such as "tell entity Z to go here and don't process anymore commands until it has gone here." The command processor would have to be called every frame, and it would have to understand complex conditions like "entity is at location" and so forth.
In Lua, it would look like this:
On the C++ size, you would resume this script once per frame until it is done. Once it returns, you know that the cutscene is over, so you can return control to the user.
Look at how simple that Lua logic is. It does exactly what it says it does. It's clear, obvious, and therefore very difficult to get wrong.
The power of coroutines is in being able to partially accomplish some task, wait for a condition to become true, then move on to the next task.
它更像是一种宗教。有些人相信协程,有些人则不相信。用例、实施和环境一起将带来好处或没有好处。
不要相信那些试图证明多核 cpu 上的协程比单线程中的循环更快的基准测试:如果它更慢,那将是一种耻辱!
如果稍后在所有核心始终处于负载状态的某些硬件上运行,它会变得更慢 - ups...
所以本身没有任何好处。
有时候用起来很方便。但如果你最终得到大量的协程让出并且状态超出了范围,你就会诅咒协程。但至少它不是协程框架,它仍然是你。
It's more like a religion. Some people believe in coroutines, some don't. The usecase, the implementation and the environment all together will result into a benefit or not.
Don't trust benchmarks which try to proof that coroutines on a multicore cpu are faster than a loop in a single thread: it would be a shame if it were slower!
If this runs later on some hardware where all cores are always under load, it will turn out to be slower - ups...
So there is no benefit per se.
Sometimes it's convenient to use. But if you end up with tons of coroutines yielding and states that went out of scope you'll curse coroutines. But at least it isn't the coroutines framework, it's still you.
我们在我正在进行的项目中使用它们。对我们来说的主要好处是,有时对于异步代码,由于某些依赖关系,某些部分按顺序运行很重要。如果使用协程,则可以强制一个进程等待另一进程完成。它们不是执行此操作的唯一方法,但它们可能比其他一些方法简单得多。
We use them on a project I am working on. The main benefit for us is that sometimes with asynchronous code, there are points where it is important that certain parts are run in order because of some dependencies. If you use coroutines, you can force one process to wait for another process to complete. They aren't the only way to do this, but they can be a lot simpler than some other methods.
这是一个非常重要的属性。我开发了一个使用它们进行计时的游戏引擎。例如,我们有一个每秒运行 10 个时钟周期的引擎,您可以 WaitTicks(x) 等待 x 个时钟周期,在用户层,您可以运行 WaitFrames(x) 等待 x 帧。
即使是专业的本机并发库也使用相同类型的让步行为。
That's a pretty important property. I worked on a game engine which used them for timing. For example, we had an engine that ran at 10 ticks a second, and you could WaitTicks(x) to wait x number of ticks, and in the user layer, you could run WaitFrames(x) to wait x frames.
Even professional native concurrency libraries use the same kind of yielding behaviour.
对于游戏开发者来说有很多很好的例子。我将在应用程序扩展空间中给出另一个。考虑这样的场景:应用程序有一个引擎,可以在 Lua 中运行用户例程,同时在 C 中执行核心功能。如果用户需要等待引擎到达特定状态(例如等待接收数据),你要么必须:
第三个选项对我来说是最容易实现的,避免了在多个平台上处理多线程的需要。它还允许用户的代码不加修改地运行,看起来好像他们调用的函数花了很长时间。
Lots of good examples for game developers. I'll give another in the application extension space. Consider the scenario where the application has an engine that can run a users routines in Lua while doing the core functionality in C. If the user needs to wait for the engine to get to a specific state (e.g. waiting for data to be received), you either have to:
The third option is the easiest for me to implement, avoiding the need to handle multi-threading on multiple platforms. It also allows the user's code to run unmodified, appearing as if the function they called took a long time.
游戏中的协程:
使用方便,在很多地方使用时容易搞砸。
只是要小心,不要在很多地方使用它。
不要让你的整个人工智能代码依赖于协程。
当引入以前不存在的状态时,协程非常适合快速修复。
这正是java所做的。睡眠()和等待()
这两个函数都是让你的游戏无法调试的最佳方法。
如果我是你,我会完全避免任何必须像协程一样使用 Wait() 函数的代码。
OpenGL API 是你应该注意的。它从不使用 wait() 函数,而是使用一个干净的状态机,该状态机确切地知道对象所处的状态。
如果您使用协程,您最终会得到如此多的无状态代码片段,调试起来肯定会很困难。
当您制作文本编辑器..银行应用程序..服务器..数据库等应用程序(不是游戏)时,协程非常有用。
当你制作一款随时可能发生任何事情的游戏时,你需要有状态,这很糟糕。
因此,在我看来协程是一种糟糕的编程方式,也是编写小型无状态代码的借口。
但这只是我。
Coroutines in a game:
Easy to use, Easy to screw up when used in many places.
Just be careful and not use it in many places.
Don't make your Entire AI code dependent on Coroutines.
Coroutines are good for making a quick fix when a state is introduced which did not exist before.
This is exactly what java does. Sleep() and Wait()
Both functions are the best ways to make it impossible to debug your game.
If I were you I would completely avoid any code which has to use a Wait() function like a Coroutine does.
OpenGL API is something you should take note of. It never uses a wait() function but instead uses a clean state machine which knows exactly what state what object is at.
If you use coroutines you end with up so many stateless pieces of code that it most surely will be overwhelming to debug.
Coroutines are good when you are making an application like Text Editor ..bank application .. server ..database etc (not a game).
Bad when you are making a game where anything can happen at any point of time, you need to have states.
So, in my view coroutines are a bad way of programming and a excuse to write small stateless code.
But that's just me.