了解赛璐珞并发
Understanding Celluloid Concurrency
以下是我的赛璐珞代码。
client1.rb
2个客户之一。 (我将其命名为客户端1)
client2.rb
2 个客户中的第 2 个。 (命名为客户 2 )
注:
上述 2 个客户端之间的唯一区别是传递给服务器的文本。 即(分别为 'client-1'
和 'client-2'
)
针对以下 2 个服务器(一次一个)测试这 2 个客户端(运行 它们并排)。我发现非常 st运行ge 结果.
server1.rb
(取自celluloid-zmqREADME.md的基本例子)
将其用作上述 2 个客户端的示例服务器导致并行执行 任务。
输出
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
注:
client2.rb 消息在 client1.rb 请求处于睡眠状态时被处理。(平行标记)
-
将此用作上述 2 个客户端的示例服务器没有导致并行执行任务。
输出
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
注:
client-2 被要求等待 60 秒,因为 client-1 正在睡觉(60 秒睡眠)
我运行以上测试多次都导致相同的行为。
任何人都可以根据上述测试的结果向我解释一下。
问题: 为什么赛璐珞要等待 60 秒才能处理其他请求,即在 server2.rb 案例中注意到的那样。?
Ruby版本
ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
使用你的要点,我验证了这个问题可以在 MRI 2.2.1
以及 jRuby 1.7.21
和 Rubinius 2.5.8
中重现...... server1.rb
和 [= 之间的区别16=]是在后者中使用了DisplayMessage
和message
class方法
在 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
不像 Server
那样 include Celluloid
)
将DisplayMessage.message
的内容放入handle_message
,如server1.rb
;或者至少进入 Server
,它在 Celluloid
范围内,并且将使用正确的 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
的范围……这就是为什么defer
和future
是我最好的建议。但是 post 我的评论中出现了一些东西:
使用 defer
或 future
推送一个任务,该任务会导致 actor 被绑定到另一个线程中。如果你使用 future
,你可以在任务完成后获得 return 值,如果你使用 defer
,你可以 fire & forget。
但更好的是,为容易被捆绑的任务创建另一个 actor,甚至将另一个 actor 汇集在一起......如果 defer
或 future
不适合你。
我很乐意回答这个问题提出的后续问题;我们有一个非常 active mailing list 和 IRC 频道。你慷慨的赏金值得称赞,但我们中的很多人会纯粹帮助你。
设法重现并修复了问题。
删除我之前的回答。
显然,问题出在sleep
。
通过将日志 "actor/kernel sleeping"
添加到 Celluloids.rb's sleep()
. 的本地副本来确认
在server1.rb
、
the call to sleep
is within server
- a class that includes Celluloid.
Thus Celluloid's implementation of sleep
overrides the native sleep
.
class Server
include Celluloid::ZMQ
...
def run
loop { async.handle_message @socket.read }
end
def handle_message(message)
...
sleep 60
end
end
注意来自 server1.rb
的日志 actor sleeping
。日志添加到Celluloids.rb's sleep()
这只会暂停 Celluloid 中的当前 "actor"
即只有当前 "Celluloid thread" 处理 client1 睡眠。
在server2.rb
、
the call to sleep
is within a different class DisplayMessage
that does NOT include Celluloid.
Thus it is the native sleep
itself.
class DisplayMessage
def self.message(message)
...
sleep 60
end
end
请注意 server2.rb
中没有任何 actor sleeping
日志。
这会暂停当前的 ruby 任务,即 ruby 服务器休眠(不只是单个 Celluloid 演员)。
修复?
In server2.rb
, the appropriate sleep
must be explicitly specified.
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
以下是我的赛璐珞代码。
client1.rb
2个客户之一。 (我将其命名为客户端1)client2.rb
2 个客户中的第 2 个。 (命名为客户 2 )
注:
上述 2 个客户端之间的唯一区别是传递给服务器的文本。 即(分别为 'client-1'
和 'client-2'
)
针对以下 2 个服务器(一次一个)测试这 2 个客户端(运行 它们并排)。我发现非常 st运行ge 结果.
server1.rb
(取自celluloid-zmqREADME.md的基本例子)将其用作上述 2 个客户端的示例服务器导致并行执行 任务。
输出
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
注:
client2.rb 消息在 client1.rb 请求处于睡眠状态时被处理。(平行标记)
-
将此用作上述 2 个客户端的示例服务器没有导致并行执行任务。
输出
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
注:
client-2 被要求等待 60 秒,因为 client-1 正在睡觉(60 秒睡眠)
我运行以上测试多次都导致相同的行为。
任何人都可以根据上述测试的结果向我解释一下。
问题: 为什么赛璐珞要等待 60 秒才能处理其他请求,即在 server2.rb 案例中注意到的那样。?
Ruby版本
ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
使用你的要点,我验证了这个问题可以在 MRI 2.2.1
以及 jRuby 1.7.21
和 Rubinius 2.5.8
中重现...... server1.rb
和 [= 之间的区别16=]是在后者中使用了DisplayMessage
和message
class方法
在 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
不像Server
那样include Celluloid
)将
DisplayMessage.message
的内容放入handle_message
,如server1.rb
;或者至少进入Server
,它在Celluloid
范围内,并且将使用正确的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
的范围……这就是为什么defer
和future
是我最好的建议。但是 post 我的评论中出现了一些东西:
使用 defer
或 future
推送一个任务,该任务会导致 actor 被绑定到另一个线程中。如果你使用 future
,你可以在任务完成后获得 return 值,如果你使用 defer
,你可以 fire & forget。
但更好的是,为容易被捆绑的任务创建另一个 actor,甚至将另一个 actor 汇集在一起......如果 defer
或 future
不适合你。
我很乐意回答这个问题提出的后续问题;我们有一个非常 active mailing list 和 IRC 频道。你慷慨的赏金值得称赞,但我们中的很多人会纯粹帮助你。
设法重现并修复了问题。
删除我之前的回答。
显然,问题出在sleep
。
通过将日志 "actor/kernel sleeping"
添加到 Celluloids.rb's sleep()
. 的本地副本来确认
在server1.rb
、
the call to
sleep
is withinserver
- a class that includes Celluloid.Thus Celluloid's implementation of
sleep
overrides the nativesleep
.
class Server
include Celluloid::ZMQ
...
def run
loop { async.handle_message @socket.read }
end
def handle_message(message)
...
sleep 60
end
end
注意来自 server1.rb
的日志 actor sleeping
。日志添加到Celluloids.rb's sleep()
在server2.rb
、
the call to
sleep
is within a different classDisplayMessage
that does NOT include Celluloid.Thus it is the native
sleep
itself.
class DisplayMessage
def self.message(message)
...
sleep 60
end
end
请注意 server2.rb
中没有任何 actor sleeping
日志。
这会暂停当前的 ruby 任务,即 ruby 服务器休眠(不只是单个 Celluloid 演员)。
修复?
In
server2.rb
, the appropriatesleep
must be explicitly specified.
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