HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

Ruby 1.9 加入纤程实现轻量级并发

  • 2007-09-05
  • 本文字数:3326 字

    阅读完需:约 11 分钟

Ruby 的线程成为人们的讨论议题已经很长时间了。未来版本的Ruby(1.9 及更高)是否将使用内核线程(kernel thread)替代用户线程(userspace threads),仍有待决定。最近,关于Ruby 中这一系列问题的另一条途径已经出现。David Flanagan 指出了 Ruby 1.9 分支中的一个新特性——纤程(Fiber)

下面是如何使用新的 Fiber 类(注意:类名可能会改变)生成(generate)一个 Fibonacci 数的无穷序列的范例。我使用“生成(generate)”这个词采用的是在 Python 中生成器(generator)的意义上的含义。Ruby 中新的纤程是半协程(semi- coroutines)。

1 fib = Fiber.new do<br></br>2 x, y = 0, 1<br></br>3 loop do<br></br>4 Fiber.yield y<br></br>5 x, y = y, x + y<br></br>6 end<br></br>7 end<br></br>8 20.times { puts fib.resume }这段代码会打印出前 20 个 Fibonacci 数。它使用了一个叫做协程(Coroutine)的概念。基本上,调用 Fiber.yield方法会停止(“挂起”)这段代码的执行(请不要把它和用于执行 block 的yield关键字相混淆)。如果你熟悉调试器,那么请想象一下对某个线程点击“挂起”按钮或者看这个线程因为断点命中而中断。fib是这个纤程的句柄,你可以用它对纤程进行操作。在第 8 行出现的fib.resume所完成的正是该代码所表达的意思:它恢复了Fiber.yield调用之后语句的执行,使该纤程恢复执行状态。

第 4 行显示了Fiber.yield带着一个参数y被调用。在某种程度上,这可以被认为是与return y相类似。子程序(subroutine)的return和协程的Fiber.yield之间的区别在于代码调用后其上下文发生的变化上。这就是说,return的意思是一个函数调用的激活格局(或者叫做栈格局)被取消分配了(deallocate),即所有的本地变量都消失了。在一个协程里,yield保存着这个激活格局并且其内部的所有数据还是存在的,因此在调用了resume方法之后,代码还能继续使用这些环境。

现在这段代码如何工作就变得很清晰了:它只是一个循环——通过相继迭代的方式计算出 Fibonacci 熟。一旦完成一次计算,它就将自己挂起,并将 CPU 的控制权让给别人。当代码性药序列中的下一个数时,它只需要恢复 Fibonacci 代码,代码运行下一次迭代,然后通过调用Fiber.yield将自己挂起让出控制权(以及下一个 Fibonacci 数)。

这是在 Ruby 1.9 分支中加入的一个相当新的特性,而看起来它的细节仍未确定。纤程这个术语对于 Windows 程序员来说,可能并不陌生。 MSDN 中是这么解释的

纤程是一个必须由应用程序手动调度的执行单位。纤程运行在调度线程的上下文中。每一个线程可以调度多个纤程。一般来说,纤程所能带来的优势是不及一个设计优良的多线程应用的。但是,对于被设计成必须调度自身线程的应用程序来说,使用纤程可以让它们的移植变得更为容易。

Ruby 1.9 VM(以前被称作YARV)的开发者笹田耕一(Sasada Koichi)就此在 ruby-core 邮件列表中给出了更详细的一些信息

这些方法名(resume/yield)来自 Lua。“transfer”来自 Modula-2,“double resume error”来自 Python 生成器。顺便说一句,我正在考虑使用“纤程(Fiber)”这个名字。目前“纤程”的意思等同于“半协程(Semi-Coroutine)”。Fiber::Core 就是协程。是的,“纤程”这个名字是来自于微软,但是它的意义等价于半协程,如 Lua 的协程和 Python 的生成器。

半协程不对称(asymmetric)协程,在它们对控制权传递的选择上收到限制。不对称协程只能将控制权传递它们的调用者,而对于协程来说,只要它们有相应的句柄,它们就可以自由地将控制传递给其它的任何一个纤程。

前面的例子展现了一个半协程是如何被当成一个生成器来用的。比如,便利地生成 Fibonacci 数。某些语言,比如 Python,在语言中支持生成器且有特定的语法支持。从引文中看来,似乎半协程(纤程)和协程(Fiber::Core)行为都是支持的。最终 Ruby 1.9 及以后的版本中会出现什么以及它会如何命名尚待分晓,但是 Ruby 语言的创造者松本行弘(Matsumoto, Yukihiro)认为他们安全:

在核心开发人员之间仍然有热烈的讨论,但是纤程(和外部迭代器)比起 Continuation,更可能留在最终的 1.9 中。

注:Continuation,一个长期在 Ruby 1.9 分支中缺席的特性。尽管对于它是否可以使用 Ruby 1.9 的核心线程实现,人们尚存疑虑,但在五月份它仍被加到Ruby 1.9 中

除了实现了控制结构,协程提供了一种使用轻量级并发(lightweight concurrency)的方式。实际上它们允许使用协同调度来实现用户线程。协程既能将控制权让给彼此,也能通过将控制权交给调度协程,让其决定下一个调度的协程这种方式来实现集中式调度。

这样,关于Ruby 1.9 转向更为重量级的内核线程的疑虑,就迎刃而解了。Ruby 1.8 线程被构建成一个用户线程系统,这样做的好处是线程管理的开销更少。创建一个内核线程要进行对操作系统的系统调用,比起对线程系统的进程内调用要费时得多。例如,JRuby 使用内核线程,但也在尝试使用线程池来弥补创建开销的性能不足。

然而,创建大量的内核线程仍旧有许多性能开销,或者会直接导致对线程有硬性数量限制或者对大量线程的支撑力不从心的操作系统出现问题。在这些情况下,一个轻量级的选择是非常有用的。如果解决方案在逻辑上可行而又简洁的话,它允许代码分配在不同的线程中执行,并且保持低水平的开销。这个方案的另一个优点是,如果需要调用一个长时间运行的操作或者进行一次系统调用,但又不能阻塞进程中所有代码的执行,那么仍然可以使用内核线程。

Erlang 采用了一个相似的方法,它也提供轻量级进程,但 Erlang 的进程不共享任何东西,与之相反,纤程共享同一个地址空间。然而,线程的存在使得人们可以采用参与式(Actor-style)的编程方式,而不需要担心额外性能开销。

在 Ruby 领域,纤程的概念也不是全新的。Rubinius 拥有 Tasks,其描述和 Ruby 1.9 的纤程类似。(InfoQ 最近有一篇新闻报导采访了 Rubinius 项目的领导人 Evan Phoenix 对 Rubinius 中这种线程模型的观点)。 MenTaLguY 就此在 ruby-core 上详细描述说

不过,在现代的并发环境中,它们 [纤程] 变得越来越有用。没有它们或者和它们相似的东西(比如 Rubinius 的 Tasks),你就得用一些丑陋的技巧才能实现轻量级并发了——你可以看看在 Scala 的 actors 类库中明确地使用 continuation-passing(指函数,而不是 Continuations),这就是它们还没有问世的时候,我们可以期望的最好的例子。 我同意,使用纤程会让 JRuby 的日子变得不是那么好过。

最后的评论提出了一个重要的观点。如果纤程被 Ruby 采用,那么对于 Ruby 在 JVM 和 CLR 上的实现,如 JRuby、XRuby、Ruby.NET 或者 IronRuby 来说,将出现一个头疼的问题。现在,它们当中还没有一个支持 Continuations,因为在这些虚拟机上操作或者读取调用栈(callstack)是非常困难,或者几乎不可能实现的。是否需要实现 Continuations 是一个很有争议的问题,然而它好像并没有给 JRuby 等带来什么大难题,因为它并没有广泛地应用在 Ruby 之中。在 Ruby 1.8 的标准库中,唯一的应用就是生成器的实现,但是,举例而言,在 JRuby 1.0 中没有使用 Continuations 也一样实现了此功能。

虽然可以避开这些问题来实现这些特性,但是问题在于这些迂回解决方法是否会引起性能下降。例如,如果调用栈必须在堆上仿真,而不是使用虚拟机的栈,这样做可能引起性能的下降,或者会阻止(JIT)编译器的的优化操作。对于非对成协程的迂回解决方法会更容易一些,因为它们使用虚拟机的栈进行方法调用。像 C#这类的语言以这种方式实现了它们的迭代器(Iterator)特性,这些迭代器允许人们用和上面的示例代码相似的方式编写生成器。

查看英文原文: Ruby 1.9 adds Fibers for lightweight concurrency - - - - - -

译者简介:仝键,网名“咖啡屋的鼠标”,普通程序员。喜欢思考,沉默时沉闷至死,说起来却又无边无际。爱好广泛常恐有贪多不精之后遗症。从小接触电脑却白白荒废十余年光阴,直至大学之后才入编程之门。如今漂泊北京寻找着自己的一片天地。感兴趣的技术领域有 Agile、Java、设计模式、Flex、Ruby 和面向对象数据库等。个人技术博客为 http://blog.csdn.net/tj19832/ 。参与 InfoQ 中文站内容建设,请邮件至 china-editorial[at]infoq.com

2007-09-05 01:303572

评论

发布
暂无评论
发现更多内容

嵌入式音频处理技术:从音频流媒体到声音识别

申公豹

嵌入式

CAN总线与嵌入式系统通信:实时性和可靠性的平衡

申公豹

嵌入式

2023 年的“云原生的演化”

卢卡多多

总结 2023

一起学Elasticsearch系列-写入原理

Java随想录

Java 大数据 elastic

文体中心场地预订小程序开发笔记一-功能设计

CC同学

用友全球司库十问(八)|集团企业如何做好资金集中化管理?

用友BIP

全球司库 资金集中管理

低功耗设计策略:延长嵌入式设备的电池寿命代码实战

申公豹

嵌入式

FPGA在嵌入式系统中的角色:加速、定制与灵活性实战与运用

申公豹

嵌入式

软件开发

Geek_8da502

Image2icon中文版激活 for Mac(icon图标设计软件) v2.18

mac大玩家j

Mac软件 图标工具

嵌入式系统入门实战:探索基本概念和应用领域

申公豹

嵌入式

嵌入式图像处理:算法、应用与性能优化

申公豹

嵌入式

被面试官PUA了:创建索引时一定会锁表?

王磊

Java 面试

强大的字体设计编辑:FontLab 8最新激活版

胖墩儿不胖y

Mac软件 字体设计 字体编辑 设计字体软件

实时任务调度与通信协议在嵌入式开发中的应用

申公豹

嵌入式

边缘智能:嵌入式系统中的神经网络应用开发实战

申公豹

嵌入式

DevSecOps端到端的安全能力构建为什么重要?

极狐GitLab

终端闲思录(5)- 终端与缓冲的关系

蓬蒿

终端 缓冲

CnosDB:深入了解时序数据处理函数

CnosDB

开源 时序数据库 CnosDB

CnosDB如何确保多步操作的最终一致性?

CnosDB

开源 时序数据库 CnosDB

SpringBoot—jasypt加解密库的使用方法。

百度搜索:蓝易云

Java Linux 运维 jasypt 云服务器

播下开源教育的种子分论坛圆满举办

开放原子开源基金会

开源

KubeWharf适合场景 开源赛道 3:深入云原生

Echo_Wish

云原生 年度总结 2023 KubeWharf 技术总结

2023 年度总结—总结我今年的AI之路-多项目实战经验谈AI发展前景

申公豹

#技术人的2023总结

ubuntu20.04上构建qemu启动linux kernel教程。

百度搜索:蓝易云

云计算 Linux ubuntu 运维 云服务器

20231225 介绍一个我解决过的难题

Luke

【AI趋势发展】 主赛道:技术人的 2023 总结

雪奈椰子

嵌入式开发-微控制器 vs. 微处理器:如何选择适合你项目的解决方案

申公豹

嵌入式

实时操作系统(RTOS)在嵌入式开发中的作用与优势实战

申公豹

嵌入式

物联网中的嵌入式系统安全代码实战与运用

申公豹

嵌入式

嵌入式系统的数据存储与管理策略

申公豹

嵌入式

Ruby 1.9加入纤程实现轻量级并发_Ruby_Werner Schuster_InfoQ精选文章