你如何使用C#进行协同例程?

时间:2010-03-22 15:07:51

标签: c# python system.reactive coroutine

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

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"

7 个答案:

答案 0 :(得分:13)

如果您想要的是一个“可观察的集合” - 也就是说,一个向您推送结果而不是让消费者拉动它们的集合 - 那么您可能希望查看Reactive Framework扩展。这是一篇关于它的文章:

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

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

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

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

答案 1 :(得分:6)

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

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

C#提供了一个名为 iterators 的混合协同功能。 C#编译器自动将迭代器代码转换为特定的状态类,局部变量成为类字段。然后,在VM级别,Yielding是普通return。只要从迭代器代码本身执行“yield”,而不是从迭代器代码调用的方法执行,就可以做到这样的事情。 C#迭代器已经涵盖了许多用例,C#设计者不愿意继续前进到continuations。一些讽刺的人热衷于声明实现全功能的延续将阻止C#像其主要的敌人Java一样高效(有效的延续是可行的,但这需要与GC和JIT编译器进行相当多的工作)。 p>

答案 2 :(得分:3)

答案 3 :(得分:3)

感谢@NickLarsen,你帮我记住了MS推出的新东西,IObservable界面。

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

答案 4 :(得分:1)

实际上,.NET没有对线程关联性做出“错误的假设”,实际上它完全将.NET级别线程的概念与OS级别线程分离。

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

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

Btw Mono 2.6包含低级别的Coroutine支持,可用于轻松实现所有更高级别的原语。

答案 5 :(得分:0)

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

我尝试在p#中使用原生光纤API通过p / inv一段时间后调用,但由于运行时的异常处理(不正确)会产生基于线程的假设,所以当异常发生时,事情就会崩溃(非常糟糕)。

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

因此,虽然真正的光纤使用案例不是主流,但它们肯定存在,如果光纤子系统中存在的错误得到解决,我们的.Net编码人员会大肆欢呼。

答案 6 :(得分:0)

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