协程的用例是什么?
协程的概念听起来很有趣,但我不知道,如果在真实的生产环境中有意义, 什么是协程的用例,可以像其他方法那样解决更优雅,更简单或更高效的问题?
真正的协程需要你的工具支持 – 它们需要由编译器来实现,并由底层框架支持。
一个现实世界中的协程的例子是在C#2.0中提供的“yield return”关键字中find的,它允许你编写一个返回多个循环值的方法。
然而,“收益回报”确实有一定的局限性,实现使用一个辅助类来捕获状态,并且它只支持一个特定的协程作为生成器(迭代器)。
在更一般的情况下,Coroutines的优势在于它们使得某些基于状态的计算更易于expression和更容易理解 – 将状态机作为一系列协同程序来实现可能比更常见的方法更为优雅 。 但是,这样做需要C#或Java中尚不存在的支持和工具。
一些描述协程的好的答案。
但是对于一个实际的用例。 采取一个networking服务器。 它具有多个同时连接,并且要安排读取和写入所有这些连接。
这可以使用协程来实现。 每个连接都是一个协同程序,它读/写less量的数据,然后对调度程序进行“放弃”控制,调度程序通过所有可用的连接循环到下一个协程(它是做同样的事情)。
其中很多,例如:
grep TODO *.c *.h | wc -l
上面的stream水线恰好是一个协程: grep
命令产生一系列的行到缓冲区, wc
命令“把它们吃掉”。 如果缓冲区填满,则grep
“阻塞”,直到缓冲区清空,如果缓冲区为空,则wc
命令等待新的input。
关于协程的事情是,它们现在最常被用在更多的约束模式中,比如上面提到的Python生成器或者stream水线。
如果您想更多地了解它们,请参阅Wikipedia文章,尤其是协程和迭代器 。
我知道这个问题已经有将近5年的时间了,但是我很惊讶,没有人提到过使用协程的游戏的用例。
为了在游戏中保持一致的帧速率,可以说60 fps,你有大约16.6ms在每一帧执行代码。 这包括物理模拟,input处理,绘图/绘画。
可以说你的方法是在每一帧中执行的。 如果你的方法需要很长时间,最终会跨越多个帧,那么你将在游戏循环中错开其余的计算,导致用户看到“jank”。
什么协同程序让你做的是不知何故时间分割这个计算,使它在每一帧运行一点点。
为了发生这种情况,协程本质上允许该方法将计算返回给“调用者”(在这种情况下为游戏循环),以便在下一次调用该方法时从其停止的地方恢复。
协程对于实现生产者/消费者模式是有用的。
例如,Python在一个名为“ 发生器”的语言特性中引入了协程,旨在简化迭代器的实现。
它们也可以用来实现协作式多任务处理,其中每个任务是一个协调器,可以产生一个调度器/反应器。
只要系统有两个或两个以上的代码,其最自然的表示将是一系列涉及大量等待的连续步骤,那么协程就可以是有用的。
例如,考虑具有LCD和键盘用户界面和调制解调器的设备,并且需要使用调制解调器来定期地调用和报告其状态,而不pipe键盘上的用户在做什么。 编写用户界面最好的方法可能是使用像“input_numeric_value(&CONV_SPEED_FORMAT,&conveyor_speed);” 当用户input一个值时会返回,处理通信的最好方法可能是使用“wait_for_carrier()”等函数。 当单元连接或确定不会时,将返回。
没有协程,UI子系统或调制解调器子系统就不得不使用状态机来实现。 使用协程可以使两个子系统以最自然的风格写成。 请注意,重要的是,如果不将子事物置于“一致”状态并调用yield(),并且不首先将事物置于“一致”状态,也不会调用yield(),这两个子系统都不会很长时间,但通常不难限制。
请注意,尽pipe可以使用全面的多任务处理,但是在任何时候共享状态被改变的情况下都需要使用锁。 由于协程切换器除了在yield()调用之外不会切换,所以任何一个例程都可以自由地改变共享状态,只要它保证所有的内容在下一个产出之前,并且为另一个例程改变状态“期间“收益()。
作为生产者/消费者行中更具体的例子,简单的批量报告程序可以实际使用协同例程。
这个例子的关键暗示就是要消耗input数据(比如parsing数据或者在账户上累计费用和支付),以及产生输出的非平凡工作。 当你有这些特点:
- 如果你可以在不同的地方“排放”工作单位,就很容易组织/理解input端代码。
- 如果可以在嵌套的控制结构中“抓取”下一个工作单元,那么组织/理解输出端代码也是很容易的。
那么协程和队列都是很好的技术。