Ruby Coroutine

Ruby1.9提供了Fiber,提供了Coroutine的功能。
Fibers作为实现轻量级合作并发的基础设施,和线程很像,提供了创建一个可以pause和resume的代码块,
但Fibers是非抢占式的,必须由程序而不是VM来调度。每一个fiber提供了4kB的栈空间可以允许fiber的
block进行深层嵌套函数调用。这是rdoc对Fiber的描述。

Ruby的Fiber就是传说中Coroutine的实现,在lua中,coroutine被广泛使用。Ruby中coroutine的
强大功能,势必会被发扬光大。Ruby中的Fiber和lua中的coroutine基本类似,都是asymmetric coroutine,
都提供了new/create,resume和yield的操作。
看个rdoc的例子:

fiber = Fiber.new do
Fiber.yield 1
2
end

puts fiber.resume #=>1
puts fiber.resume #=>2
puts fiber.resume #=>FiberError: dead fiber called

Fiber有三种状态:suspend、running、dead。Fiber被new之后处于suspend状态,当调用resume时开始运行(running),coruntine执行的block完毕或者出错时dead.
上面的例子,我们new一个fiber,这时fiber处于suspend,当我们调用resume的时候,开始执行,Fiber.yield
放弃当前的执行控制,将控制权交给resume的调用者。第二次调用fiber.resume时从Fiber.yeild 1返回,开始执行下一句代码,这时这个fiber执行完毕,所以第三次调用时fiber已经dead。

resume参数可以是任何个,第一次调用的时候作为block的参数,后面的调用作为yield的返回值。


fiber = Fiber.new do |first|
second = Fiber.yield first + 2
end

puts fiber.resume 10 #12
puts fiber.resume 14 #14
puts fiber.resume 18 #FiberError: dead fiber called



通过上面我们已经熟悉了Coroutine,下面我们创建一个fib数列生成器的coroutine,小小练习一下:


fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x,y = y,x+y
end
end
20.times { puts fib.resume }


coroutinue最常用的场合就是合作并发的程序,producer和consumer是最典型的方式:
一个偶数生产者和消费者:

def even_producer #even number producer
Fiber.new do
value = 0
loop do
Fiber.yield value
value += 2
end
end
end

def consumer(source)
Fiber.new do
10.times do
next_val = source.resume
puts next_val
end
end
end

consumer(even_producer).resume

在加点东东,加个过滤器,我只想要是3的倍数的偶数:

def multiple_of_three_filter(source)
Fiber.new do
loop do
next_val = source.resume
Fiber.yield next_val if next_val % 3 == 0
end
end
end
consumer(multiple_of_three_filter(even_producer)).resume

我们看到可以像Unix的管道一样将多个coroutine连结起来,我们甚至可以定义像Unix shell一样的
管道操作符[url="http://pragdave.blogs.pragprog.com/pragdave/2007/12/pipelines-using.html"]Pipelines Using Fibers in Ruby 1.9[/url]:
even_producer | multiple_of_three_filter | consumer

class PipelineElement
attr_accessor :source

def initialize
@fiber_delegate = Fiber.new do
process
end
end

def |(other)
other.source = self
other
end

def resume
@fiber_delegate.resume
end

def process
while value = input
handle_value(value)
end
end

def handle_value(value)
output(value)
end

def input
source.resume
end

def output(value)
Fiber.yield(value)
end
end

class EvenProducer < PipelineElement
def process
value = 0
loop do
output(value)
value += 2
end
end
end

class MultipleOf < PipelineElement
def initialize(factor)
@factor = factor
super()
end

def handle_value(value)
output(value) if value % @factor == 0
end
end

even_producer = EvenProducer.new
multiples_of_three_filter = MultipleOf.new(3)
multiples_of_seven_filter = MultipleOf.new(7)
pipeline = even_producer | multiples_of_three_filter | multiples_of_seven_filter

10.times do
puts pipeline.resume
end


使用shell的管道每次需要创建一个进程,而使用coroutine每次只需要创建一个轻量级的Fiber

关于asymmetric coroutine和symmetric coroutine
1)asymmetric coroutine:提供了两种控制转换操作:(re)invoking和suspending一个coroutine,suspending将控制流程返回给coroutine的调用者。这个过程和调用和被调用的routine类似(routine在现代语言中被实现为函数、方法、过程,又叫subroutine),所以这种方式非常容易被使用传统编程语言的程序员接受,这也是大多数语言实现asymmetric coroutine的原因之一,其他的原因:比如lua是和c/c++结合比较紧密的语言,如果实现symmetric coroutine则要求c/c++支持coroutine,这显然是不现实的,另外symmetric的也很容易用asymmetric描述。
2)symmetric coroutine:只提供了一种控制上述操作的操作。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值