写点什么

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:303862

评论

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

云小课|GaussDB如何进行性能调优

华为云开发者联盟

数据库 后端 华为云 企业号 2 月 PK 榜 华为云开发者联盟

金兔迎福报、新春第一炮【2022 中国开源年度报告】!

腾源会

开源

快速入门pandas进行数据挖掘数据分析[多维度排序、数据筛选、分组计算、透视表](一)

汀丶人工智能

Python 数据挖掘 数据分析 pandas

大文件传输软件的优势有哪些?-镭速传输

镭速

程序员培训后怎么能够在一线城市找到工作?

小谷哥

我发现买不起自己出版的图书了,这到底是咋回事?

冰河

程序员 并发编程 高并发 架构师 编程开发

MoBYv2AL :结合 BOYL 和 MoCo 的主动学习算法

Zilliz

2/8 19:00 直播 | StarRocks 实战系列第一期--部署&导入

StarRocks

数据库

构建工具tsup入门第一部分

小鑫同学

前端 编译 工具链

大数据软件开发培训中心哪家好

小谷哥

对话阿里云叔同:如何看待 2022 年云原生的发展,2023 年有哪些值得关注的技术?

阿里巴巴中间件

阿里云 云原生

干货|常用3D建模软件有哪些?

Finovy Cloud

3DMAX Autodesk Maya 3D软件

解锁极狐GitLab 自动化功能:Webhook 触发 Pipeline 与计划流水线

极狐GitLab

DevOps pipeline ChatOps webhook ChatGPT

品牌不得不投放户外LED广告的原因

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

深入浅出学习透析Nginx服务器的架构分析及原理分析「底层技术原理+运作架构机制」

C++后台开发

nginx 负载均衡 中间件 后端开发 Linux服务器开发

Intel官方回应:处理器降价消息不实!全力减少库存

科技之家

跬智信息 (Kyligence) 入选2022年度上海市优质大数据服务供应商目录

Kyligence

大数据 数据分析

Apipost预执行脚本使用教程

徐天

前端技术培训学习的就业怎么样?

小谷哥

小白科普丨何为树、二叉树和森林

华为云开发者联盟

开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

前端培训机构毕业后该注意什么?

小谷哥

深度 | Web 3.0时代去中心化IM 的挑战与思考

环信

Web3.0 环信im

软件测试/测试开发 | app自动化测试(Android)—Capability 使用进阶

测试人

软件测试 自动化测试 测试开发 appium app自动化测试

基于ModelArts进行流感患者密接排查

华为云开发者联盟

人工智能 华为云 行人检测 企业号 2 月 PK 榜 华为云开发者联盟

前端程序员就业方向有哪些?

小谷哥

C#/VB.NET 在Excel中添加水印

在下毛毛雨

C# .net Excel 添加水印

看板:自我管理的高效工具!

敏捷开发

项目管理 软件开发 看板

为啥要对jvm做优化

华为云开发者联盟

开发 华为云 企业号 2 月 PK 榜 华为云开发者联盟

基于 Flink+Pravega 的游戏服务器监控与调节系统设计

Apache Flink

大数据 flink 实时计算

官宣:计算中间件 Apache Linkis 正式毕业成为 Apache 顶级项目

腾源会

开源

青云 KubeSphere 与 OpenCloudOS 完成技术兼容互认证

OpenCloudOS

云原生 操作系统 KubeSphere

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