Rails线程测试db锁

时间:2012-09-28 21:02:32

标签: ruby-on-rails ruby multithreading

假设我们想测试数据库是否被锁定..

$transaction = Thread.new  {
    Rails.logger.debug 'transaction process start'
    Inventory.transaction do
      inventory.lock!
      Thread.stop
      inventory.units_available=99
      inventory.save
    end
  }
  $race_condition = Thread.new  {
    Rails.logger.debug 'race_condition process start'
    config = ActiveRecord::Base.configurations[Rails.env].symbolize_keys
    config[:flags] = 65536 | 131072 | Mysql2::Client::FOUND_ROWS
    begin
      connection = Mysql2::Client.new(config)

      $transaction.run
      $transaction.join
    rescue NoMethodError
    ensure
      connection.close if connection
    end
  }
  Rails.logger.debug 'main process start'
  $transaction.join
  Rails.logger.debug 'main process after transaction.join'
  sleep 0.1 while $transaction.status!='sleep' 
  Rails.logger.debug 'main process after sleep'
  $race_condition.join
  Rails.logger.debug 'main process after race_condition.join'

理论上,我认为它会执行事务线程,然后等待(Thread.stop),然后主进程会看到它正在休眠,并启动竞争条件线程(它将尝试更改数据)当它实际工作时锁定的表)。然后竞争条件将在事务线程完成后继续。

痕迹是什么奇怪的

main process start
transaction process start
race_condition process start

来自nodejs,似乎线程并不完全像用户友好..但是,必须有一种方法来完成这项工作。

是否有更简单的方法来锁定数据库,然后尝试使用其他线程更改它?

2 个答案:

答案 0 :(得分:0)

对于任何使其成为“互斥”的资源,您需要使用 Mutex 类并使用同步方法在一个线程使用时锁定资源他们。你必须做这样的事情:

semaphore = Mutex.new

并在Thread实例中使用它。

$transaction = Thread.new  { 
  semaphore.synchronize{
  # Do whatever you want with the *your shared resource*
  }
}

这样可以防止任何死锁。

希望这会有所帮助。

答案 1 :(得分:0)

Thread.new自动启动线程。 但这并不意味着它正在执行。 这取决于操作系统,ruby或jruby,多少核心等。

在您的示例中,主线程一直运行到 $ transaction.join, 只有这样你的交易线程才会开始。 它仍然运行Thread.stop,然后你的'$ race_condition'线程启动,因为其他两个都被阻止(它可能已经开始)

这样就解释了你的日志。

你有两个$ transaction.join
他们等到线程退出,但一个线程只能退出一次...... 我不知道接下来会发生什么,也许第二次电话会永远等待。

对于您的测试,您需要某种显式同步,以便我们的race_thread在transaction_thread处于事务中间时准确写入。你可以用Mutex做到这一点,但更好的是某种消息传递。以下博文可能有所帮助:

http://www.engineyard.com/blog/2011/a-modern-guide-to-threads/