Ali*_*har 49
一些很好的答案描述了协程是什么.
但对于实际的用例.拿一个Web服务器.它具有多个同时连接,并且它希望安排读取和写入所有连接.
这可以使用协程来实现.每个连接都是一个协程,它读取/写入少量数据,然后"调出"控制到调度程序,调度程序在我们循环遍历所有可用连接时传递给下一个协同程序(它做同样的事情).
Cha*_*tin 21
很多,例如:
grep TODO *.c *.h | wc -l
Run Code Online (Sandbox Code Playgroud)
上面的管道正是一个协程:该grep命令生成一系列行到缓冲区的行,wc命令"吃掉它们"; 如果缓冲区填满,则grep"阻塞"直到缓冲区清空,如果缓冲区为空,则wc命令等待新输入.
关于协同程序的事情是它们现在最常用于更多约束模式,如所提到的Python生成器,或者作为管道.
如果您想更多地了解它们,请参阅Wikipedia文章,尤其是关于协同程序和迭代器的文章.
num*_*ati 20
我知道问题被问到这已经差不多5年了,但是我很惊讶没有人提到游戏的用例,其中协同程序被大量用于基本上时间切片计算.
为了在游戏中保持一致的帧速率,假设为60 fps,每帧中执行代码大约需要16.6ms.这包括物理模拟,输入处理,绘图/绘画.
让我们说你的方法在每一帧都执行.如果你的方法花费很长时间并最终跨越多个帧,你将在游戏循环中错开其余的计算,这导致用户看到"jank"(帧速率突然下降).
你可以做的协程是以某种方式对这个计算进行时间切片,以便它在每个帧中运行一点点.
为了实现这一点,协程基本上允许该方法将计算"产生"回"调用者"(在这种情况下是游戏循环),以便下次调用该方法时,它从它停止的地方恢复.
Bev*_*van 18
真正的协同程序需要您的工具支持 - 它们需要由编译器实现并由底层框架支持.
使用C#2.0中提供的"yield return"关键字找到了Coroutines的一个真实示例,它允许您编写一个返回多个循环值的方法.
然而,"yield return"确实有局限性 - 实现使用辅助类来捕获状态,它只支持coroutine的特定情况作为生成器(迭代器).
在更一般的情况下,Coroutines的优势在于它们使某些基于状态的计算更容易表达和更容易理解 - 将状态机实现为一组协程可以比更常见的方法更优雅.但是,这样做需要C#或Java中尚不存在的支持和工具.
sup*_*cat 10
只要系统有两个或多个代码,其中最自然的表示形式将是一系列涉及大量等待的连续步骤,协同程序就非常有用.
例如,考虑具有LCD和键盘用户界面和调制解调器的设备,并且它需要使用调制解调器来定期呼叫和报告其状态,而与键盘上的用户正在做什么无关.编写用户界面的最好方法可能是使用"input_numeric_value(&CONV_SPEED_FORMAT,&conveyor_speed);"之类的函数.当用户输入值时将返回,并且处理通信的最佳方式可能是使用"wait_for_carrier();"之类的函数.当设备已连接或确定它不会返回时将返回.
如果没有协同程序,则必须使用状态机来实现UI子系统或调制解调器子系统.使用协同程序可以使两个子系统以最自然的方式编写.请注意,重要的是,如果不将事物置于"一致"状态并调用yield(),也不调用yield()而不将事物置于"一致"状态,那么子系统都不会长时间运行,但通常不难满足这些限制.
请注意,虽然可以使用完整的多任务处理,但只要共享状态发生变化,就需要在整个地方使用锁.由于协程切换器除了在yield()调用之外不会切换任何东西,所以任何例程都可以自由地改变共享状态,只要它确保在下一次产生之前按顺序排列所有内容,并为其他例程改变状态做好准备"在"收益率"期间.
作为生产者/消费者行中更具体的例子,像简陋的批量报告程序这样简单的东西实际上可以使用协同例程.
该示例的关键提示是使用输入数据进行非常重要的工作(例如,解析数据或累积帐户上的费用和付款),以及产生输出的非平凡工作.当你有这些特点时:
协同程序和队列都是很好的技术可供您使用.