【问题标题】:Ensure thread safety in the code确保代码中的线程安全
【发布时间】:2016-04-16 14:09:01
【问题描述】:

我正在尝试编写一个多线程代码来实现一项耗时过多的任务的并行性。这是它的外观:

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} }

【问题讨论】:

    标签: ruby multithreading


    【解决方案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 这样的库,它们具有线程安全数据结构和其他可以提供帮助的模式。

    【讨论】:

    • 不管怎样,Logger 是线程安全的,所以输出应该符合预期。
    • 太好了,谢谢@coreyward。如果有人想要快速的经验测试,Array.new(500) {|i| Thread.new { 1000.times {|j| Rails.logger.info "#{i}-#{j}"}}}.each(&:join)
    猜你喜欢
    • 2011-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多