在阅读有关线程安全的ZeroMQ FAQ时,发现了一些问题。
我的多线程程序在 ZeroMQ 库内部的奇怪位置经常崩溃。我做错了什么?
ZeroMQ 套接字不是线程安全的。这在指南中有详细介绍。
简单来说,套接字不应该在线程之间共享。我们建议为每个线程创建一个专用套接字。
对于那些无法为每个线程创建专用套接字的情况,只有在每个线程访问套接字之前执行完整的内存屏障后,才可以共享套接字。大多数编程语言都支持 Mutex 或 Spinlock,在您的代表下执行完整的内存屏障。
我的多线程程序在 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])
这也可能导致数据排序问题。
我的观察是否正确?
注意:请保留HTML标记。
Operating system ( MacOS, Linux and CentOS )
Ruby - MRI 2.2.2/2.3.0
async
会创建一个线程?你可以使用 fibers 进行非阻塞调用。你需要阅读文档才能确定,甚至需要查看源代码。 - tadman