代码之家  ›  专栏  ›  技术社区  ›  WeNeedAnswers

如何使用c执行共同例程?

  •  9
  • WeNeedAnswers  · 技术社区  · 15 年前

    在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 回复  |  直到 11 年前
        1
  •  13
  •   Eric Lippert    14 年前

    如果你想要的是一个“可观察的集合”——也就是说,一个将结果推给你而不是让消费者拉动它们的集合——那么你可能想看看反应式框架扩展。这是一篇关于它的文章:

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

    现在,正如您所注意到的,如果您有可用的协程,那么您可以轻松地构建“push”和“pull”样式的迭代器。(或者,正如托马斯指出的那样,你也可以用continuations来构建它们。)在当前的c版本中,我们没有真正的协同(或continuations)。然而,我们非常关注用户所感受到的痛苦 异步编程 .

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

    更新:我们最近宣布,我们将向下一个版本的c和vb添加类似于协程的异步控制流。你可以在我们的社区技术预览版中自己尝试,你可以下载 here .

        2
  •  6
  •   Thomas Pornin    15 年前

    C没有 一般的 共同惯例。一般的共同例程是指共同例程有自己的堆栈,即它可以调用其他方法,这些方法可以“产生”值。实现一般的联合例程需要使用堆栈来做一些聪明的事情,可能包括在堆上分配堆栈帧(包含局部变量的隐藏结构)。这是可以做到的,有些语言做到了(例如scheme),但是要做到正确有些困难。而且,许多程序员发现这个特性很难理解。

    通用的共同例程可以用线程模拟。每个线程都有自己的堆栈。在共同例程设置中,两个线程(初始调用方和共同例程的线程)将交替控制,它们实际上永远不会同时运行。“yield”机制是两个线程之间的交换,因此代价很高(同步、通过os内核和调度程序的往返…)。此外,内存泄漏还有很大的空间(必须显式地“停止”co例程,否则等待的线程将永远保持不变)。因此,很少这样做。

    C提供了一个叫做 迭代器 . C编译器自动将迭代器代码转换为特定的状态类,局部变量成为类字段。那么,在vm级别,屈服是一个普通的 return . 只要“yield”是从迭代器代码本身执行的,而不是从迭代器代码调用的方法执行的,这样的事情是可行的。c迭代器已经覆盖了许多用例,而c设计人员不愿意继续深入到 continuations . 一些讥讽的人热衷于说明实现全功能的连续性会阻止C语言与它的原敌Java一样高效(有效的延续是可行的,但是这需要GC和JIT编译器相当多的工作)。

        4
  •  3
  •   WeNeedAnswers    15 年前

    谢谢@nicklarsen,你帮我记住了ms引入的新东西,iobservable接口。

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

        5
  •  1
  •   Gluber    14 年前

    实际上.NET并没有对线程关联性做出“错误的假设”,事实上它完全将.NET级线程的概念与操作系统级线程分离开来。

    您需要做的是将逻辑.NET线程状态与光纤关联(因为您需要CLR宿主API,但不需要自己编写主机,您可以直接从自己的应用程序中使用所需的),并且,锁定跟踪、异常处理等所有操作都能正常工作。

    下面是一个例子: http://msdn.microsoft.com/en-us/magazine/cc164086.aspx

    BTW Mono 2.6包含低级别协同程序支持,可用于轻松实现所有高级原语。

        6
  •  0
  •   user382965    14 年前

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

    不久前,我试图通过p/invoke在c_中使用本机fiber a p i,但是由于运行时的异常处理(错误地)做出了基于线程的假设,因此当发生异常时,事情就(严重地)中断了。

    基于光纤的协同程序api的一个“杀手级应用”是游戏编程;某些类型的ai需要一个“轻量级”线程,您可以随意对其进行时间切片。例如,游戏行为树要求能够“脉冲”决策代码每一帧,允许人工智能代码在决策片段向上时协同地向调用者屈服。这可以用硬线程实现,但要复杂得多。

    因此,虽然真正的光纤用例并不是主流,但它们确实存在,如果光纤子系统中现有的bug得到解决,那么我们.net程序员中的一小部分人会非常高兴。

        7
  •  0
  •   Kendar    11 年前

    我试着开发一个完整的库来管理只有一个线程的协程。困难的部分是在协程中调用协程…并返回参数,但最后我得到了一个相当好的结果 here . 唯一的警告是阻塞I/O操作必须通过任务进行,并且所有的“return”必须替换为“yield return”。 有了基于这个库的应用服务器,我几乎可以将使用基于iis的标准async/await发出的请求翻一番。(在github上查找node.cs和node.cs.musicstore以在家中进行尝试)