了解赛璐珞并发

以下是我的赛璐珞代码。

  1. client1.rb 2个客户端之一。 (我把它命名为客户1)

  2. client2.rb 2个客户中的第2个。 (命名为客户2)

注意:

上述两个客户端之间唯一的区别是传递给服务器的文本。 ie(分别是'client-1''client-2'

在测试这2个客户端(通过并排运行)对后续2个服务器(一次一个)。 我发现很奇怪的结果

  1. server1.rb取自celluloid-zmq的README.md的基本示例

    使用此作为上述2个客户端的示例服务器导致并行执行任务。

OUTPUT

 ruby server1.rb Received at 04:59:39 PM and message is client-1 Going to sleep now Received at 04:59:52 PM and message is client-2 

注意:

client1.rb请求处于hibernate状态时处理client2.rb消息。( 并行标记

  1. server2.rb

    使用此作为上述2个客户端的示例服务器并未导致并行执行任务。

OUTPUT

 ruby server2.rb Received at 04:55:52 PM and message is client-1 Going to sleep now Received at 04:56:52 PM and message is client-2 

注意:

客户端-2被要求等待60秒,因为客户端1正在睡觉(60秒睡眠)

我多次运行上面的测试都导致了相同的行为。

任何人都可以从上述测试的结果中解释我。

问题:为什么赛璐珞会在处理其他请求之前等待60秒,即在server2.rb情况下注意到。

Ruby版本

ruby -v

ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

使用你的要点,我validation了这个问题可以在MRI 2.2.1以及jRuby 1.7.21Rubinius 2.5.8重现…… server1.rbserver2.rb之间的区别在于使用DisplayMessagemessage类后者的方法。


DisplayMessage使用sleep不在Celluloid范围内。

当在server1.rb中使用sleep ,它实际上正在使用Celluloid.sleep ,但是当在server2.rb中使用它时,它使用Kernel.sleep …它将Server的邮箱锁定,直到60秒过去。 这可以防止将来处理该actor的方法调用,直到邮箱再次处理消息(对actor的方法调用)。

有三种方法可以解决这个问题:

  • 使用defer {}future {}块。

  • 显式调用Celluloid.sleep而不是sleep (如果没有显式调用Celluloid.sleep ,使用sleep将最终调用Kernel.sleep因为DisplayMessage不像Kernel.sleep那样include Celluloid

  • DisplayMessage.message的内容放入handle_message如同server1.rb ; 或者至少进入Celluloid范围内的Server ,并使用正确的sleep


defer {}方法:

 def handle_message(message) defer { DisplayMessage.message(message) } end 

Celluloid.sleep方法:

 class DisplayMessage def self.message(message) #de ... Celluloid.sleep 60 end end 

不是真正的范围问题; 这是关于异步的。

重申一下,更深层次的问题不是sleep的范围……这就是为什么deferfuture是我最好的建议。 但是在我的评论中发布一些内容:

使用deferfuture推送一个会导致actor绑定到另一个线程的任务。 如果您使用future ,则可以在任务完成后获得返回值,如果您使用defer ,则可以触发并忘记。

但更好的是,为那些倾向于被束缚的任务创建另一个角色,甚至将其他演员聚集在一起…如果deferfuture不适合你。

我非常乐意回答这个问题提出的后续问题; 我们有一个非常活跃的邮件列表和IRC频道。 你慷慨的赏金是值得称道的,但我们很多人会纯粹帮助你。

管理重现并解决问题。 删除我以前的答案。 显然,问题在于sleep 通过将日志"actor/kernel sleeping"到Celluloids.rb的sleep()的本地副本来确认。


server1.rb

sleep呼叫server内 – 包括赛璐珞的类。

因此,赛璐珞的sleep实施会覆盖原生sleep

 class Server include Celluloid::ZMQ ... def run loop { async.handle_message @socket.read } end def handle_message(message) ... sleep 60 end end 

请注意日志操作actor sleepingserver1.rb actor sleeping 日志添加到Celluloids.rb的sleep()

这仅暂停Celluloid中的当前“actor”,即仅处理client1睡眠的当前“Celluloid线程”。


server2.rb

sleep调用是在不包括Celluloid的不同类DisplayMessage中。

因此,它是原生sleep本身。

 class DisplayMessage def self.message(message) ... sleep 60 end end 

请注意来自server2.rb的任何actor sleeping日志的ABSENCE。

这会暂停当前的ruby任务,即ruby服务器hibernate(不只是单个Celluloid actor)。


修复?

server2.rb ,必须显式指定适当的sleep

 class DisplayMessage def self.message(message) puts "Received at #{Time.now.strftime('%I:%M:%S %p')} and message is #{message}" ## Intentionally added sleep to test whether Celluloid block the main process for 60 seconds or not. if message == 'client-1' puts 'Going to sleep now'.red # "sleep 60" will invoke the native sleep. # Use Celluloid.sleep to support concurrent execution Celluloid.sleep 60 end end end