协程的用例是什么?

Mne*_*nth 45 use-case coroutine

协程的概念听起来非常有趣,但我不知道,如果它在真正的生产环境中有意义吗?协程的用例是什么,与其他方法一样,可以解决更优雅,更简单或更高效的问题?

Ali*_*har 49

一些很好的答案描述了协程是什么.

但对于实际的用例.拿一个Web服务器.它具有多个同时连接,并且它希望安排读取和写入所有连接.

这可以使用协程来实现.每个连接都是一个协程,它读取/写入少量数据,然后"调出"控制到调度程序,调度程序在我们循环遍历所有可用连接时传递给下一个协同程序(它做同样的事情).

  • 我不知道为什么在没有+1这么久的情况下这种情况就会萎缩.一个协程驱动的Web服务器,假设这些例程是为零碎计算而设计的,在吞吐量方面会对重量级线程Web服务器产生影响,并且比状态机管理的服务器更容易理解. (7认同)

Cha*_*tin 21

很多,例如:

grep TODO *.c *.h | wc -l
Run Code Online (Sandbox Code Playgroud)

上面的管道正是一个协程:该grep命令生成一系列行到缓冲区的行,wc命令"吃掉它们"; 如果缓冲区填满,则grep"阻塞"直到缓冲区清空,如果缓冲区为空,则wc命令等待新输入.

关于协同程序的事情是它们现在最常用于更多约束模式,如所提到的Python生成器,或者作为管道.

如果您想更多地了解它们,请参阅Wikipedia文章,尤其是关于协同程序迭代器的文章.

  • 我从来没有想过!杰出的! (2认同)

num*_*ati 20

我知道问题被问到这已经差不多5年了,但是我很惊讶没有人提到游戏的用例,其中协同程序被大量用于基本上时间切片计算.

为了在游戏中保持一致的帧速率,假设为60 fps,每帧中执行代码大约需要16.6ms.这包括物理模拟,输入处理,绘图/绘画.

让我们说你的方法在每一帧都执行.如果你的方法花费很长时间并最终跨越多个帧,你将在游戏循环中错开其余的计算,这导致用户看到"jank"(帧速率突然下降).

你可以做的协程是以某种方式对这个计算进行时间切片,以便它在每个帧中运行一点点.

为了实现这一点,协程基本上允许该方法将计算"产生"回"调用者"(在这种情况下是游戏循环),以便下次调用该方法时,它从它停止的地方恢复.


Bev*_*van 18

真正的协同程序需要您的工具支持 - 它们需要由编译器实现并由底层框架支持.

使用C#2.0中提供的"yield return"关键字找到了Coroutines的一个真实示例,它允许您编写一个返回多个循环值的方法.

然而,"yield return"确实有局限性 - 实现使用辅助类来捕获状态,它只支持coroutine的特定情况作为生成器(迭代器).

在更一般的情况下,Coroutines的优势在于它们使某些基于状态的计算更容易表达和更容易理解 - 将状态机实现为一组协程可以比更常见的方法更优雅.但是,这样做需要C#或Java中尚不存在的支持和工具.


dda*_*daa 12

协同程序对于实现生产者/消费者模式很有用.

例如,Python在称为generator的语言特性中引入了协同程序,旨在简化迭代器的实现.

它们对于实现协作式多任务处理也很有用,其中每个任务都是一个可以产生调度程序/反应堆的协程.

  • 生成器非常方便,在今天的Python中广泛使用.与使用对象编写的等效代码相比,它们可以生成更简单,更易读的代码,将状态信息放在成员中.但它们并非完整的共同惯例,并且相比之下有其局限性. (2认同)

sup*_*cat 10

只要系统有两个或多个代码,其中最自然的表示形式将是一系列涉及大量等待的连续步骤,协同程序就非常有用.

例如,考虑具有LCD和键盘用户界面和调制解调器的设备,并且它需要使用调制解调器来定期呼叫和报告其状态,而与键盘上的用户正在做什么无关.编写用户界面的最好方法可能是使用"input_numeric_value(&CONV_SPEED_FORMAT,&conveyor_speed);"之类的函数.当用户输入值时将返回,并且处理通信的最佳方式可能是使用"wait_for_carrier();"之类的函数.当设备已连接或确定它不会返回时将返回.

如果没有协同程序,则必须使用状态机来实现UI子系统或调制解调器子系统.使用协同程序可以使两个子系统以最自然的方式编写.请注意,重要的是,如果不将事物置于"一致"状态并调用yield(),也不调用yield()而不将事物置于"一致"状态,那么子系统都不会长时间运行,但通常不难满足这些限制.

请注意,虽然可以使用完整的多任务处理,但只要共享状态发生变化,就需要在整个地方使用锁.由于协程切换器除了在yield()调用之外不会切换任何东西,所以任何例程都可以自由地改变共享状态,只要它确保在下一次产生之前按顺序排列所有内容,并为其他例程改变状态做好准备"在"收益率"期间.


joe*_*ely 6

作为生产者/消费者行中更具体的例子,像简陋的批量报告程序这样简单的东西实际上可以使用协同例程.

该示例的关键提示是使用输入数据进行非常重要的工作(例如,解析数据或累积帐户上的费用和付款),以及产生输出的非平凡工作.当你有这些特点时:

  • 如果您可以在不同的地方"发出"工作单元,则很容易组织/理解输入端代码.
  • 如果它可以"抓取"嵌套控制结构中的下一个工作单元,那么组织/理解输出端代码同样很容易.

协同程序和队列都是很好的技术可供您使用.