【问题标题】:Simple parallelism with Fibers?与 Fibers 的简单并行性?
【发布时间】:2016-06-01 13:18:55
【问题描述】:

我正在尝试使用两个 Fiber 验证基本的产量/恢复模式。但是,产量/恢复机制似乎不起作用。我尝试了一些变体,但找不到我的错误。

预期的事件顺序:

  1. 启动线程(时间 0)
  2. 等到第一次返回(时间 +2 秒)
  3. 等到第二次返回(时间 +2 秒)
  4. 完成(时间 > +3 秒)

试试#1

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    fib1 = Fiber.current
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    fib1.resume(1)
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    fib2 = Fiber.current
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    fib2.resume(2)
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

sleep 1
puts "Done @ #{Time.now}."

这会导致:

Waiting @ 2016-06-01 06:15:52 -0700.
Fiber1 starting @ 2016-06-01 06:15:52 -0700.
f1 back @ 2016-06-01 06:15:52 -0700 - .
Fiber2 starting @ 2016-06-01 06:15:52 -0700.
f2 back @ 2016-06-01 06:15:52 -0700 - .
Done @ 2016-06-01 06:15:53 -0700.

添加第二个 resume 会导致 FiberError。

尝试#2

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    1
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    2
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

sleep 1
puts "Done @ #{Time.now}."

这会导致:

Waiting @ 2016-06-01 10:53:17 -0700.
Fiber1 starting @ 2016-06-01 10:53:17 -0700.
f1 back @ 2016-06-01 10:53:17 -0700 - .
Fiber2 starting @ 2016-06-01 10:53:17 -0700.
f2 back @ 2016-06-01 10:53:17 -0700 - .
Done @ 2016-06-01 10:53:18 -0700.

在这两种情况下,开始/结束时间相同,并且不返回结果。

【问题讨论】:

  • 为什么您的光纤会自行调用 resume?
  • @FrederickCheung - 在没有 Fiber.current.resume 的情况下尝试过。仍然没有产生预期的结果,即:1)启动线程,2)等到第一次返回,3)等到第二次返回,4)完成。

标签: ruby fibers


【解决方案1】:

Fibers 本身不会让您实现并行性,至少在不使用某种回调机制(例如 eventmachine 框架)的情况下不会。

您所写的只是尝试在代码块之间交错同步执行。您没有得到预期序列的原因是,虽然您确实模拟了开球,但您从未在屈服后恢复纤维。

您可能会发现以下帖子很有用,尤其是最后的示例:
http://schmurfy.github.io/2011/09/25/on_fibers_and_threads.html

另一个显示光纤相互转移控制的示例:
https://gist.github.com/aprescott/971008

这应该会给你预期的结果:

#!/usr/bin/env ruby

require 'fiber'

f1 = Fiber.new do
    puts "Fiber1 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber1 done @ #{Time.new}."
    1
end
f2 = Fiber.new do
    puts "Fiber2 starting @ #{Time.new}."
    Fiber.yield
    sleep 2
    puts "Fiber2 done @ #{Time.new}."
    2
end

puts "Waiting @ #{Time.new}."
r1 = f1.resume
puts "f1 back @ #{Time.new} - #{r1}."
r2 = f2.resume
puts "f2 back @ #{Time.new} - #{r2}."

# Resume right after the yield in the fiber block and 
# execute until it encounters another yield or the block ends.
puts "Resuming f1"
f1.resume 
puts "Resuming f2"
f2.resume

sleep 1
puts "Done @ #{Time.now}."

输出:

Waiting @ 2016-06-05 00:35:29 -0700.
Fiber1 starting @ 2016-06-05 00:35:29 -0700.
f1 back @ 2016-06-05 00:35:29 -0700 - .
Fiber2 starting @ 2016-06-05 00:35:29 -0700.
f2 back @ 2016-06-05 00:35:29 -0700 - .
Resuming f1
Fiber1 done @ 2016-06-05 00:35:31 -0700.
Resuming f2
Fiber2 done @ 2016-06-05 00:35:33 -0700.
Done @ 2016-06-05 00:35:34 -0700.

【讨论】:

  • 谢谢,@vaibhav。 'sleep'实际上将被调用两个使用 EM::Synchrony.sync 序列化的 Defferable 结果所取代,所以 O 认为应该处理并行执行方面。我仍然需要验证。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-17
  • 2018-01-13
  • 2011-03-16
  • 2019-03-07
相关资源
最近更新 更多