Ruby 数据映射器、互斥体和线程的奇怪bug?
我有一个满是URL的数据库,需要定期测试HTTP响应时间。我希望有许多工作线程在任何时候都在数据库中搜索一个最近没有测试过的URL,如果找到了,就测试它 当然,这可能会导致多个线程从数据库中拦截相同的URL。我不要这个。所以,我尝试使用互斥来防止这种情况发生。我意识到在数据库级别还有其他选项(乐观锁定、悲观锁定),但我至少更愿意弄清楚为什么这不起作用 看看我写的测试代码:Ruby 数据映射器、互斥体和线程的奇怪bug?,ruby,multithreading,concurrency,locking,datamapper,Ruby,Multithreading,Concurrency,Locking,Datamapper,我有一个满是URL的数据库,需要定期测试HTTP响应时间。我希望有许多工作线程在任何时候都在数据库中搜索一个最近没有测试过的URL,如果找到了,就测试它 当然,这可能会导致多个线程从数据库中拦截相同的URL。我不要这个。所以,我尝试使用互斥来防止这种情况发生。我意识到在数据库级别还有其他选项(乐观锁定、悲观锁定),但我至少更愿意弄清楚为什么这不起作用 看看我写的测试代码: threads = [] mutex = Mutex.new 50.times do |i| threads <
threads = []
mutex = Mutex.new
50.times do |i|
threads << Thread.new do
while true do
url = nil
mutex.synchronize do
url = URL.first(:locked_for_testing => false, :times_tested.lt => 150)
if url
url.locked_for_testing = true
url.save
end
end
if url
# simulate testing the url
sleep 1
url.times_tested += 1
url.save
mutex.synchronize do
url.locked_for_testing = false
url.save
end
end
end
sleep 1
end
end
threads.each { |t| t.join }
threads=[]
mutex=mutex.new
是我的50.5倍|
线程错误,:times_tested.lt=>150)
如果url
url.locked_for_testing=true
url.save
结束
结束
如果url
#模拟测试url
睡眠1
url.times_tested+=1
url.save
mutex.do
url.locked_for_testing=false
url.save
结束
结束
结束
睡眠1
结束
结束
threads.each{t|t.join}
当然这里没有真正的URL测试。但最终,每个URL的“测试次数”应该等于150次,对吗
(我基本上只是想确保互斥体和工作线程的心态正常工作)
但每次我运行它时,这里和那里的一些奇怪的URL都会被测试次数减少到一个更低的数字,比如说37,然后锁定在“true”上进行测试
现在,就我的代码所知,如果任何URL被锁定,它将不得不解锁。所以我不明白为什么有些URL会像那样“冻结”
没有例外,我尝试添加begin/sure,但没有任何效果
有什么想法吗?我会用一个队列和一个主控器来拉你想要的东西。如果您只有一个主控器,那么您可以控制访问的内容。这并不完美,但它不会因为并发性而崩溃,记住,如果你不锁定数据库,互斥锁就不会真正帮助你,因为其他东西会访问数据库 完全未经测试的代码
require 'thread'
queue = Queue.new
keep_running = true
# trap cntrl_c or something to reset keep_running
master = Thread.new do
while keep_running
# check if we need some work to do
if queue.size == 0
urls = URL.all(:times_tested.lt => 150)
urls.each do |u|
queue << u.id
end
# keep from spinning the queue
sleep(0.1)
end
end
end
workers = []
50.times do
workers << Thread.new do
while keep_running
# get an id
id = queue.shift
url = URL.get(id)
#do something with the url
url.save
sleep(0.1)
end
end
end
workers.each do |w|
w.join
end
需要“线程”
queue=queue.new
保持运行=真
#陷阱cntrl_c或重置的东西保持_运行
master=Thread.new do
当你继续跑的时候
#检查一下我们是否需要做一些工作
如果queue.size==0
URL=URL.all(:times\u tested.lt=>150)
URL.each do|u|
队列中存在未同步的url.save,这将导致问题。此外,这似乎也不会从线程的使用中受益。@pguardiario您能解释一下不同步的保存是如何导致问题的吗?我的逻辑是,其他任何东西都不应该触及该URL,因为它在该点已经被锁定。此外,这也得益于线程,因为网络连接速度很慢,所以一台计算机可以同时测试多个URL。是的,但是线程共享一个数据库连接。数据库操作应该总是同步的,因为没有理由不同步。你可能正在耗尽连接pool@FrederickCheung你能详细说明一下吗?我不明白