探索AIGC电商新纪元,火山引擎《云上新视界》公开课等你来报名! 了解详情
写点什么

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

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

    阅读完需:约 11 分钟

《企业级 Agents 开发实战营》重磅上线,10 周带你进行工具、对话及多模态等不同类型 Agents 工程化开发实战!

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

评论

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

作为一名后台开发人员,你必须知道的两种过滤器

C++后台开发

后台开发 后端开发 Linux服务器开发 C/C++后台开发 C/C++开发

图的存储结构与方法(二)

乔乔

7月月更

界面设计四大原则

空城机

设计模式 7月月更

Java开发环境配置 / Vscode搭建

攻城狮杰森

Java jdk 7月月更

一时跳槽一时爽,一直跳槽一直爽?

KEY.L

7月月更

Protocol buffers 的问题和滥用

HoneyMoose

SpringBoot项目使用Tomcat为什么让大厂禁止?

Java程序员

Java 程序员 Sprint Boot

OpenIM重大优化-消息按需加载 一致性缓存 uniapp发布

Geek_1ef48b

多线程&高并发(全网最新:面试题+导图+笔记)面试手稳心不慌

冉然学Java

Java 编程 多线程并发 高并发系统 资料分享

MySQL数据库索引

技术小生

索引 7月月更

面试官:MySQL 数据库查询慢,除了索引问题还可能是什么原因?

Java全栈架构师

Java MySQL 数据库 面试 后端

参与开源社区还有证书拿?

玩转Devop和研发效能DevStream/DevLake

GitHub 开源 开发者 证书

李宏毅《机器学习》丨7. Conclusion(总结)

AXYZdong

7月月更

OSI模型第一层:物理层,基石般的存在!

wljslmz

物理层 网络技术 OSI模型 7月月更

IntelliJ IDEA使用

GalaxyCreater

Java IDEA

阿里二面:什么是CAS?

Java永远的神

Java 程序员 面试 程序人生 CAS

自动驾驶产品化竞备开启:百度Apollo如何定义量产车?

脑极体

全新出品!Github总榜排行第七的SpringCloud生态全栈笔记我粉了

Java全栈架构师

Java 程序员 面试 微服务 SpringCloud

springMvc参数获取

沃德

Java 7月月更

12张图+6K字图解ZGC垃圾回收器及调优技巧

程序员小毕

程序员 面试 程序人生 ZGC JVM

7大专题详解SpringBoot,阿里这套SpringBoot全栈笔记真香

Java永远的神

Java 程序员 面试 程序人生 springboot

类的基础

GalaxyCreater

Docker安装Elasticsearch、ik分词器、可视化工具

宁在春

Docker Elastic Stack 7月月更

【Go实现】实践GoF的23种设计模式:观察者模式

元闰子

Go 设计模式 观察者模式 Go 语言

做一个有职业操守的软件匠人

Bruce Talk

技术 敏捷 TDD Agile

基于 Web SDK 实现视频通话场景 | 声网 SDK 教程

声网

视频 SDK 教程

mysql进阶(十九)SQL语句如何精准查找某一时间段的数据

No Silver Bullet

MySQL 7月月更 精确查找

Vue Router 守卫

程序员海军

Vue 7月月更

python小知识-代码规范最佳实践

AIWeker

7月月更 pyhon小知识

深入浅出边缘云 | 1. 概述

俞凡

架构 边缘计算 网络 深入浅出边缘云

@Entity 里面的 JPA 注解

Damon

7月月更

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