偶然阅读有关线程安全的ZeroMQ 常见问题解答。
我的多线程程序在 ZeroMQ 库中的奇怪地方不断崩溃。我究竟做错了什么?
ZeroMQ 套接字不是线程安全的。《指南》中对此进行了详细介绍。
简短的版本是套接字不应在线程之间共享。我们建议为每个线程创建一个专用套接字。
对于每个线程的专用套接字不可行的情况,当且仅当每个线程在访问套接字之前执行完整的内存屏障时,才可以共享套接字。大多数语言都支持互斥锁或自旋锁,它们将代表您执行完整的内存屏障。
我的多线程程序在 ZeroMQ 库中的奇怪地方不断崩溃。
我究竟做错了什么?
以下是我的以下代码:
Celluloid::ZMQ.init
module Scp
module DataStore
class DataSocket
include Celluloid::ZMQ
def pull_socket(socket)
@read_socket = Socket::Pull.new.tap do |read_socket|
## IPC socket
read_socket.connect(socket)
end
end
def push_socket(socket)
@write_socket = Socket::Push.new.tap do |write_socket|
## IPC socket
write_socket.connect(socket)
end
end
def run
pull_socket and push_socket and loopify!
end
def loopify!
loop {
async.evaluate_response(read_socket.read_multipart)
}
end
def evaluate_response(data)
return_response(message_id,routing,Parser.parser(data))
end
def return_response(message_id,routing,object)
data = object.to_response
write_socket.send([message_id,routing,data])
end
end
end
end
DataSocket.new.run
现在,有几件事我不清楚:
1) 假设async
产生一个新的Thread
(每次)并且write_socket
在所有线程之间共享并且ZeroMQ表示它们的套接字不是线程安全的。我当然看到了write_socket
线程安全问题。
(顺便说一句,到目前为止,在所有端到端测试中都没有遇到这个问题。)
问题1:我对此的理解是否正确?
为了解决这个问题,ZeroMQ 要求我们使用 Mutex、Semaphore 来实现这一点。
这导致问题2
2) 上下文切换。
给定线程应用程序可以随时进行上下文切换。查看 ffi-rzmq 代码Celluloid::ZMQ
.send()
内部调用send_strings(),内部调用send_multiple()
问题 2:上下文切换可以发生在(任何地方)内部(甚至在关键部分)(这里)[ https://github.com/chuckremes/ffi-rzmq/blob/master/lib/ffi-rzmq/socket.rb#L510 ]
这也可能导致数据排序问题。
我的以下观察是否正确?
笔记:
Operating system ( MacOS, Linux and CentOS )
Ruby - MRI 2.2.2/2.3.0