0

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

$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,似乎线程并不完全像用户友好.. 但是,必须有一种方法来完成这项工作。

有没有更简单的方法来锁定数据库,然后尝试用不同的线程更改它?

4

2 回答 2

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/

于 2012-09-29T00:10:24.077 回答
0

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

semaphore = Mutex.new

并在 Thread 实例中使用它。

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

这样您就可以防止任何死锁。

希望这可以帮助。

于 2012-09-28T21:57:40.600 回答