10

在 python 中,yield 关键字可以在推送和拉取上下文中使用,我知道如何在 c# 中执行拉取上下文,但我将如何实现推送。我发布了我试图从 python 在 c# 中复制的代码:

def coroutine(func):
  def start(*args,**kwargs):
    cr = func(*args,**kwargs)
    cr.next()
    return cr
  return start

@coroutine
def grep(pattern):
  print "Looking for %s" % pattern
  try:
    while True:
      line = (yield)
      if pattern in line:
        print line,
  except GeneratorExit:
    print "Going away. Goodbye"
4

7 回答 7

14

如果你想要的是一个“可观察的集合”——也就是说,一个将结果推送给你而不是让消费者拉取结果的集合——那么你可能想要研究 Reactive Framework 扩展。这是一篇关于它的文章:

http://www.infoq.com/news/2009/07/Reactive-Framework-LINQ-Events

现在,正如您所注意到的,如果您有可用的协程,您可以轻松构建“推”和“拉”风格的迭代器。(或者,正如 Thomas 所指出的,您也可以使用延续来构建它们。)在当前版本的 C# 中,我们没有真正的协程(或延续)。但是,我们非常关心用户对异步编程的痛苦。

将基于 Fiber 的协程实现为一流的语言特性是一种可能用于使异步编程更容易的技术,但这只是我们目前正在研究的许多想法中的一种。如果你有一个非常棒的场景,协程比其他任何东西都做得更好——包括反应式框架——那么我很想听听更多关于它的信息。我们掌握的关于人们在异步编程中面临的实际问题的数据越真实,我们就越有可能提出一个好的解决方案。谢谢!

更新:我们最近宣布我们将在下一版本的 C# 和 VB 中添加类似协程的异步控制流。您可以使用我们的社区技术预览版自行尝试,您可以在此处下载。

于 2010-03-22T18:12:39.440 回答
7

C# 没有通用的协程。一个通用的协程是协程有自己的堆栈,即它可以调用其他方法并且这些方法可以“产生”值。通用协程的实现需要用堆栈做一些聪明的事情,可能包括在堆上分配堆栈帧(包含局部变量的隐藏结构)。可以做到这一点,某些语言可以做到这一点(例如 Scheme),但正确地做到这一点有点棘手。此外,许多程序员发现该功能难以理解。

一般的协同程序可以用线程来模拟。每个线程都有自己的堆栈。在协同程序设置中,两个线程(初始调用者和协同程序的线程)将交替控制,它们实际上永远不会同时运行。然后,“yield”机制是两个线程之间的交换,因此它很昂贵(同步,通过操作系统内核和调度程序的往返......)。此外,还有很大的内存泄漏空间(协程必须明确“停止”,否则等待的线程将永远停留)。因此,很少这样做。

C# 提供了一个被称为迭代器的低级协同程序功能。C# 编译器自动将迭代器代码转换为特定的状态类,局部变量成为类字段。在 VM 级别,Yielding 是一个普通的return. 只要“yield”是从迭代器代码本身执行的,而不是从迭代器代码调用的方法执行,这样的事情是可行的。C# 迭代器已经涵盖了许多用例,而 C# 设计者不愿意在延续的道路上走得更远。一些讽刺的人热衷于声明,实现全功能的延续会阻止 C# 像它的宿敌 Java 一样高效(高效的延续是可行的,但这需要 GC 和 JIT 编译器进行相当多的工作)。

于 2010-03-22T15:41:04.390 回答
3

也许这会有所帮助。

http://blogs.msdn.com/ericlippert/archive/2009/07/23/iterator-blocks-part-five-push-vs-pull.aspx

于 2010-03-22T15:12:31.173 回答
3

谢谢@NickLarsen,你帮我记住了 MS 引入的新东西,IObservable 接口。

链接http://msdn.microsoft.com/en-us/library/dd783449(VS.100).aspx

于 2010-03-22T15:24:26.947 回答
1

实际上 .NET 并没有对线程亲和性做出“不正确的假设”,实际上它完全将 .NET 级线程的概念与 OS 级线程分离。

您需要做的是将逻辑 .NET 线程状态与您的纤程关联(为此您需要 CLR 托管 API,但您不需要自己编写主机,您可以直接使用您自己的应用程序所需的那些)和一切,锁定跟踪,异常处理再次正常工作。

可以在此处找到一个示例:http: //msdn.microsoft.com/en-us/magazine/cc164086.aspx

顺便说一句,Mono 2.6 包含低级协程支持,可用于轻松实现所有更高级别的原语。

于 2010-07-07T22:08:14.217 回答
0

我很想看到一个基于光纤的 .Net API。

不久前,我尝试通过 p/invoke 在 C# 中使用本机 Fiber API,但是由于运行时的异常处理(错误地)做出了基于线程的假设,所以当异常发生时,事情就坏了(严重)。

基于光纤的协程 API 的一个“杀手级应用”是游戏编程。某些类型的 AI 需要一个“轻量级”线程,您可以随意对它进行时间切片。例如,游戏行为树需要能够在每一帧“脉冲”决策代码,从而允许 AI 代码在决策切片启动时协作返回给调用者。这可以用硬线程来实现,但是要复杂得多。

因此,虽然真正的光纤用例不是主流,但它们肯定存在,如果解决了光纤子系统中现有的错误,我们中的一小部分 .Net 编码人员会欢呼雀跃。

于 2010-07-04T05:58:49.437 回答
0

好吧,我尝试开发一个完整的库来管理只有一个线程的协程。困难的部分是在协程中调用协程......并返回参数,但最后我在这里达到了一个很好的结果。唯一的警告是阻塞 I/O 操作必须通过任务进行,并且所有“return”必须替换为“yield return”。使用基于此库的应用程序服务器,我能够将使用基于 IIS 的标准异步/等待发出的请求增加近一倍。(在github上找Node.Cs和Node.Cs.Musicstore在家试试)

于 2014-04-22T18:18:01.413 回答