我正在尝试编写一个多线程代码来实现花费太多时间的任务的并行性。以下是它的外观:
class A
attr_reader :mutex, :logger
def initialize
@reciever = ZeroMQ::Queue
@sender = ZeroMQ::Queue
@mutex = Mutex.new
@logger = Logger.new('log/test.log')
end
def run
50.times do
Thread.new do
run_parallel(@reciever.get_data)
end
end
end
def run_parallel(data)
## Define some local variables.
a , b = data
## Log some data to file.
logger.info "Got #{a}"
output = B.get_data(b)
## Send output back to zermoq.
mutex.synchronize { @sender.send_data(output} }
end
end
需要确保代码是线程安全的。跨线程共享和更改数据(如@
,@@
,$
,没有适当的互斥)可能会导致线程安全问题。
我不确定是否将数据传递给方法,这也会导致线程安全问题。换句话说,如果我没有使用任何run_parallel
mutex
,我是否必须确保@
中的部分代码必须包含在@@
中,方法内部$
?或者给定的互斥定义是否足够?
mutex.synchronize { @sender.send_data(output} }
答案 0 :(得分:1)
每当你在线程上下文中运行时,你必须知道(对于一个简单的启发式)任何不是局部变量的东西。我在你的代码中看到了这些潜在的问题:
run_parallel(@reciever.get_data)
get_data
线程是否安全?您已同步send_data
,并且它们都是ZeroMQ::Queue
,所以我猜不会。
output = B.get_data(b)
此调用线程是否安全?如果它只是从b
中抽出一些东西,你就可以了,但如果它在B
中使用状态或者调用其他任何有状态的东西,那你就麻烦了。
logger.info "Got #{a}"
@coreyward指出Logger
是线程安全的,所以这不是问题。只要确保坚持puts
,这将使你的输出变得黯然失色。
一旦你进入@sender.send_data
的互斥锁,你就是安全的,假设代码中的其他任何地方都没有访问@sender
。当然,你抛出的synchronize
越多,你的线程就越会阻塞并失去性能,所以你需要找到平衡设计。
尽你所能使代码正常运行:尝试仅使用本地状态并编写没有副作用的方法。随着您的任务变得更加复杂,像concurrent-ruby这样的库具有线程安全的数据结构和其他可以提供帮助的模式。