Skip to content

Instantly share code, notes, and snippets.

@u2
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save u2/78615c0feb51237d22ea to your computer and use it in GitHub Desktop.
Save u2/78615c0feb51237d22ea to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# 有关 Fiber 的解释: (按照数据流的方向分为两部分)
# 在 `主线程' 中使用 resume 方法来启动(或继续执行)一个 `纤程'.
# 1. 第一次调用 fiber.resume, 会启动一个纤程,
# 如果 resume 调用时提供了实参, 会作为代码块形参传入代码块.
# 2. 如果非第一次调用 fiber.resume, 即, `恢复' 一个纤程, 会做两件事:
# - 从上次离开纤程的那个位置(调用 Fiber.yield 离开纤程的那个位置), 恢复纤程的执行.
# - 如果 resume 调用时提供了实参, 会传入 block, 作为 Fiber.yield 的返回值.
# 在纤程代码块中, 使用 Fiber.yield 暂停一个纤程, 并返回一个值给 resume 方法.
# 1. 如果存在 Fiber.yield VALUE, 则返回 VALUE 给 resume 方法, 不提供参数返回 nil.
# 2. 如果没有 Fiber.yield 语句, 返回 block 的返回值.
# 换个角度来理解纤程: Fiber 就是将 `Block' 和 `调用 Block 的代码' 分在两个线程中执行,
# 并且指定了线程的切换条件而已, 如果在 Fiber 的 block 中, 从来不指定 Fiber.yield,
# 就和在同一个线程中通过一个 block 来执行一段代码, 不存在任何区别了.
# 只要你真正的理解了Ruby 中的 block, 就基本上理解了纤程, 只是稍稍复杂一点点.
require 'eventmachine'
require 'em-http'
require 'fiber'
do_request_fiber = Fiber.new do
puts "Setting up HTTP request #1"
f = Fiber.current
http = EventMachine::HttpRequest.new('http://www.google.com/').get :timeout => 10
http.callback { EM.stop; f.resume(http) }
http.errback { EM.stop; f.resume(http) }
puts "Fetched page #1: #{Fiber.yield.response_header.status}"
end
EventMachine.run do
do_request_fiber.resume
end
# 流程分析:
# 1. l39, 在 EM.run 中, 通过 do_request_fiber.resume 启动一个新的 fiber.
# 2. l27, puts "Setting up HTTP request #1".
# 3. l29, 在 block 中获取当前 fiber 对象到 f.
# 4. l30, 向 www.google.com 发送一个 get 请求. 然后立即返回.
# 5. l32-33, 为刚才的那个请求, 注册 callback 和 errback 两个 callback,
# 当 http 被正常响应时, callback 被执行, 否则 errback 被执行.
# 注意, 我这里故意将 EventMachine.stop 写在第一行, 这会有一个错觉,
# callback 刚开始执行就 stop 了, 其实不是这样, 稍后有解释.
# 6. l35, 执行 puts "Fetched ...", 当执行到 Fiber.yield.response_header.status 时,
# 只执行了一半, 即: Fiber.yield, 就被 yield 回了 EM 主线程, .response_header.status 根本没有被执行.
# 7. EM 主线程 loop 仍在继续. 不过, 这并不会让 resume 方法被重新调用, 我觉得这是最大的一个猫腻了.
# 因为 EM.run 中的所有代码, 在 EM 初始化之后, Reactor 循环开始之前, 被执行的.
# 8. 在经过 n 个 EM loop 之后, 终于 http.callback 激发条件被满足(www.google.com 发回了响应),
# 此时, callback 中的代码 `在主线程中' 被执行.
# 9. l32, 通过 EM.stop 注册在下一轮 loop 时, 终止 EM 的 reactor 循环. 本轮仍会继续, 这回答了 #5 的问题.
# 10. l32, 调用 f.resume(http), 恢复之前暂停的纤程 `从暂停位置' 继续向后执行,
# 并且传递 http 对象给 Fiber.yield.
# 11. 回到纤程中, 在 Fiber.yield(它的值是: http 对象)之上调用 response_header.status. puts 结果.
# 12. 在下一个 loop 中, EM 的 Reactor 循环被终止, EM 退出.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment