ruby 纤程程序中的控制流
control flow in ruby fiber program
我知道纤程是协作线程。纤程可以控制执行上下文,而抢占式线程则不能。纤维可以屈服控制,这意味着纤维可以在定义明确的地方开始和停止。
显然,之所以在事件中使用纤程ruby是为了清理由reactor模式引起的嵌套块。
但是我很难理解下面使用 fiber 的脚本的控制流程。
def http_get(url)
f = Fiber.current
http = EventMachine::HttpRequest.new(url).get
# resume fiber once http call is done
http.callback { f.resume(http) }
http.errback { f.resume(http) }
return Fiber.yield
end
EventMachine.run do
Fiber.new{
page = http_get('http://www.google.com/')
puts "Fetched page: #{page.response_header.status}"
if page
page = http_get('http://www.google.com/search?q=eventmachine')
puts "Fetched page 2: #{page.response_header.status}"
end
}.resume
end
我的理解方式:
1) EM 开始其事件循环
2) 创建纤程,然后调用resume。传递给 new 的代码块是立即执行还是在调用 resume 后执行?
3) http_get 是第一次调用。它执行异步事件(在 linux 上使用 select、轮询或 epoll)。我们设置异步事件的事件处理程序(在回调方法中)。然后 Fiber 自愿将控制权交给 EventMachine 所在的线程(主线程)。但是,一旦回调被调用,它将通过 f.resume(http) 取回控制权。但是在这个简化的例子中,我应该把我自己的回调代码放在 f.resume(http) 之后吗?因为现在看来 f.resume(http) 只是 returns 控制光纤,什么都不做。
我认为在 yield 之后发生的事情是控制转到 EventMachine,它进入它的事件循环。所以第二个 http_get 还没有被调用。现在,一旦回调被调用,控制权就会返回到 Fiber(我们只使用一个 Fiber.new,所以我假设所有这些中只有一个 Fiber 实例)。但是第二个 http_get 什么时候被调用?
让我看看是否可以为您解答。我正在添加行号以帮助描述:
01: def http_get(url)
02: f = Fiber.current
03: http = EventMachine::HttpRequest.new(url).get
04:
05: # resume fiber once http call is done
06: http.callback { f.resume(http) }
07: http.errback { f.resume(http) }
08:
09: return Fiber.yield
10: end
11:
12: EventMachine.run do
13: Fiber.new{
14: page = http_get('http://www.google.com/')
15: puts "Fetched page: #{page.response_header.status}"
16:
17: if page
18: page = http_get('http://www.google.com/search?q=eventmachine')
19: puts "Fetched page 2: #{page.response_header.status}"
20: end
21: }.resume
22: end
- 第21行开始执行第14-20行代码的Fiber
- Fiber 代码似乎在执行以下操作:第 14 行检查我们是否可以在
google.come
上执行 GET。在第 17 行,它检查是否有来自 http_get
的有效响应,然后,在第 18 行执行下一个请求以搜索字符串 eventmachine
.
- 当第 21 行的
.resume
导致 Fiber 执行开始时,将执行第 14 行调用 http_get
方法。
- 第 02 到 07 行设置异步 HTTP GET 请求和回调。
- 第 09 行将控制权交还给 EventMachine。
- 当第 03 行的异步 HTTP GET 调用异步完成执行并导致第 06 行或第 07 行的回调之一后,第 13 行到第 20 行创建的原始 Fiber 取回控制权。
- 现在 Fiber 执行从第 15 行恢复。第 06/07 行的回调传递了对
http
对象的引用,该对象现在在第 14 行中用变量 page
引用,随后在第 15 行转储 HTTP 请求状态。
- 随着 Fiber 继续执行,它会检查
page
是否为真值,然后继续并再次调用 http_get
,但使用新的 URL。请注意,如果代码 if page
是 nil
,则代码 if page
可能永远不会执行,因为第 15 行会在未检查 nil
. 的情况下访问 page
。
- 重复类似的过程 - 第 02 到 07 行设置 HTTP GET 调用,第 09 行将控制权交还给 EventMachine。
- 一段时间后,调用其中一个回调并在 Fiber 重新获得控制权时执行第 19 行。
- 第19行执行后,Fiber会死
希望澄清问题。
至于使用附加逻辑处理 HTTP GET 的响应,我想您可以用一些有意义的处理逻辑替换 puts
。此示例中的 puts
似乎正在处理响应,回调主要用于恢复 Fiber。
我知道纤程是协作线程。纤程可以控制执行上下文,而抢占式线程则不能。纤维可以屈服控制,这意味着纤维可以在定义明确的地方开始和停止。
显然,之所以在事件中使用纤程ruby是为了清理由reactor模式引起的嵌套块。
但是我很难理解下面使用 fiber 的脚本的控制流程。
def http_get(url)
f = Fiber.current
http = EventMachine::HttpRequest.new(url).get
# resume fiber once http call is done
http.callback { f.resume(http) }
http.errback { f.resume(http) }
return Fiber.yield
end
EventMachine.run do
Fiber.new{
page = http_get('http://www.google.com/')
puts "Fetched page: #{page.response_header.status}"
if page
page = http_get('http://www.google.com/search?q=eventmachine')
puts "Fetched page 2: #{page.response_header.status}"
end
}.resume
end
我的理解方式:
1) EM 开始其事件循环
2) 创建纤程,然后调用resume。传递给 new 的代码块是立即执行还是在调用 resume 后执行?
3) http_get 是第一次调用。它执行异步事件(在 linux 上使用 select、轮询或 epoll)。我们设置异步事件的事件处理程序(在回调方法中)。然后 Fiber 自愿将控制权交给 EventMachine 所在的线程(主线程)。但是,一旦回调被调用,它将通过 f.resume(http) 取回控制权。但是在这个简化的例子中,我应该把我自己的回调代码放在 f.resume(http) 之后吗?因为现在看来 f.resume(http) 只是 returns 控制光纤,什么都不做。
我认为在 yield 之后发生的事情是控制转到 EventMachine,它进入它的事件循环。所以第二个 http_get 还没有被调用。现在,一旦回调被调用,控制权就会返回到 Fiber(我们只使用一个 Fiber.new,所以我假设所有这些中只有一个 Fiber 实例)。但是第二个 http_get 什么时候被调用?
让我看看是否可以为您解答。我正在添加行号以帮助描述:
01: def http_get(url)
02: f = Fiber.current
03: http = EventMachine::HttpRequest.new(url).get
04:
05: # resume fiber once http call is done
06: http.callback { f.resume(http) }
07: http.errback { f.resume(http) }
08:
09: return Fiber.yield
10: end
11:
12: EventMachine.run do
13: Fiber.new{
14: page = http_get('http://www.google.com/')
15: puts "Fetched page: #{page.response_header.status}"
16:
17: if page
18: page = http_get('http://www.google.com/search?q=eventmachine')
19: puts "Fetched page 2: #{page.response_header.status}"
20: end
21: }.resume
22: end
- 第21行开始执行第14-20行代码的Fiber
- Fiber 代码似乎在执行以下操作:第 14 行检查我们是否可以在
google.come
上执行 GET。在第 17 行,它检查是否有来自http_get
的有效响应,然后,在第 18 行执行下一个请求以搜索字符串eventmachine
. - 当第 21 行的
.resume
导致 Fiber 执行开始时,将执行第 14 行调用http_get
方法。 - 第 02 到 07 行设置异步 HTTP GET 请求和回调。
- 第 09 行将控制权交还给 EventMachine。
- 当第 03 行的异步 HTTP GET 调用异步完成执行并导致第 06 行或第 07 行的回调之一后,第 13 行到第 20 行创建的原始 Fiber 取回控制权。
- 现在 Fiber 执行从第 15 行恢复。第 06/07 行的回调传递了对
http
对象的引用,该对象现在在第 14 行中用变量page
引用,随后在第 15 行转储 HTTP 请求状态。 - 随着 Fiber 继续执行,它会检查
page
是否为真值,然后继续并再次调用http_get
,但使用新的 URL。请注意,如果代码if page
是nil
,则代码if page
可能永远不会执行,因为第 15 行会在未检查nil
. 的情况下访问 - 重复类似的过程 - 第 02 到 07 行设置 HTTP GET 调用,第 09 行将控制权交还给 EventMachine。
- 一段时间后,调用其中一个回调并在 Fiber 重新获得控制权时执行第 19 行。
- 第19行执行后,Fiber会死
page
。
希望澄清问题。
至于使用附加逻辑处理 HTTP GET 的响应,我想您可以用一些有意义的处理逻辑替换 puts
。此示例中的 puts
似乎正在处理响应,回调主要用于恢复 Fiber。