假设我们想测试数据库是否被锁定..
$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,似乎线程并不完全像用户友好..但是,必须有一种方法来完成这项工作。
是否有更简单的方法来锁定数据库,然后尝试使用其他线程更改它?
答案 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/