Ruby 互斥锁不工作,使用队列工作。为什么?
在本例中,我希望同步两个Ruby 互斥锁不工作,使用队列工作。为什么?,ruby,concurrency,synchronization,mutex,Ruby,Concurrency,Synchronization,Mutex,在本例中,我希望同步两个put,以使输出为abababab…,在输出上没有任何双as或bs 我有三个例子:使用队列、在内存中使用互斥体以及在文件中使用互斥体。队列示例可以很好地工作,但互斥体不行 我不是在寻找工作代码。我想了解为什么使用队列是有效的,而使用互斥是无效的。根据我的理解,它们应该是等价的。 队列示例:工作 def a Thread.new do $queue.pop puts "a" b end end def b Thread.new do
put
,以使输出为abababab…
,在输出上没有任何双a
s或b
s
我有三个例子:使用队列、在内存中使用互斥体以及在文件中使用互斥体。队列示例可以很好地工作,但互斥体不行
我不是在寻找工作代码。我想了解为什么使用队列是有效的,而使用互斥是无效的。根据我的理解,它们应该是等价的。
队列示例:工作
def a
Thread.new do
$queue.pop
puts "a"
b
end
end
def b
Thread.new do
sleep(rand)
puts "b"
$queue << true
end
end
$queue = Queue.new
$queue << true
loop{a; sleep(rand)}
互斥变量示例:不工作
def a
Thread.new do
$mutex.flock(File::LOCK_EX)
puts "a"
b
end
end
def b
Thread.new do
sleep(rand)
puts "b"
$mutex.flock(File::LOCK_UN)
end
end
MUTEX_FILE_PATH = '/tmp/mutex'
File.open(MUTEX_FILE_PATH, "w") unless File.exists?(MUTEX_FILE_PATH)
$mutex = File.new(MUTEX_FILE_PATH,"r+")
loop{a; sleep(rand)}
def a
Thread.new do
$mutex.lock
puts "a"
b
end
end
def b
Thread.new do
sleep(rand)
puts "b"
$mutex.unlock
end
end
$mutex = Mutex.new
loop{a; sleep(rand)}
在互斥锁示例中,在方法
b
中创建的线程会休眠一段时间,打印b
,然后尝试解锁互斥锁。这是不合法的,线程无法解锁互斥锁,除非它已经持有该锁,如果您尝试以下操作,则会引发ThreadError:
m = Mutex.new
m.unlock
结果:
release.rb:2:in'unlock':尝试解锁未锁定的互斥锁(ThreadError)
从release.rb:2:in`'
在您的示例中不会看到这一点,因为。您可以使用–如果您添加
Thread.abort_on_exception = true
在文件的顶部,您将看到如下内容:
a
B
with mutex.rb:15:“解锁”中:尝试解锁未锁定的互斥锁(ThreadError)
使用互斥锁从。rb:15:in'block in b'
(您可能会看到多个a
,但只有一个b
)
在a
方法中,创建获取锁的线程,打印a
,调用另一个方法(创建新线程并立即返回),然后终止。它似乎没有很好的文档记录,但当一个线程终止时,它会释放它拥有的所有锁,因此在这种情况下,锁几乎立即被释放,允许其他a
线程运行
总的来说,锁的作用不大。它根本不阻止b
线程运行,虽然它确实阻止两个a
线程同时运行,但一旦持有它的线程退出,它就会被释放
我想你可能在想,而Ruby医生说他们在想
Ruby在标准库中不提供信号量,但它确实提供了。(该链接指向较旧的2.0.0文档。Ruby 2.1+默认需要线程
标准库,而这一移动似乎导致当前文档不可用。还要注意,Ruby还有一个单独的库(我认为)添加了相同的功能(互斥体和条件变量)以更面向对象的方式。)
使用条件变量和互斥量可以控制线程之间的协调。显示了一种可能的方法(尽管我认为他的解决方案的启动方式存在竞争条件)
如果您查看(这也是到2.0.0的链接,线程库在最新版本中已转换为C,Ruby版本更容易理解),您可以看到它是通过和实现的。当您在队列示例中的
a
线程中调用$queue.pop
时,您最终的调用方式与Uri Agassi在其方法a
中的应答调用$cv.wait($mutex)
相同。类似地,当您呼叫$queue简短回答时
您对互斥的使用不正确。使用队列
,您可以先用一个线程填充,然后用另一个线程填充弹出
,但不能用一个线程锁定互斥锁
,然后用另一个线程解锁
正如@matt所解释的,有一些微妙的事情正在发生,比如互斥锁自动解锁和你看不到的无声异常
互斥体的常用方式
互斥体用于访问特定的共享资源,如变量或文件。因此,变量和文件的同步允许同步多个线程。互斥锁本身并不真正同步线程
例如:
thread\u a
和thread\u b
可以通过共享布尔变量(如true\u a\u false\u b
进行同步
每次使用该布尔变量时,您都必须访问、测试和切换该布尔变量,这是一个多步骤的过程
必须确保此多步骤过程以原子方式进行,即不中断。此时您将使用互斥锁。下面是一个简单的例子:
基于文件的版本的问题尚未正确解决。
它不起作用的原因是f.flock(File::LOCK_EX)
在对同一文件f
多次调用时不会阻塞。
这可以通过以下简单的顺序程序进行检查:
require 'thread'
MUTEX_FILE_PATH = '/tmp/mutex'
$fone= File.new( MUTEX_FILE_PATH, "w")
$ftwo= File.open( MUTEX_FILE_PATH)
puts "start"
$fone.flock( File::LOCK_EX)
puts "locked"
$fone.flock( File::LOCK_EX)
puts "so what"
$ftwo.flock( File::LOCK_EX)
puts "dontcare"
它可以打印除dontcare之外的所有内容
因此,基于文件的程序无法工作,因为
$mutex.flock(File::LOCK_EX)
从不阻拦。@sawa看看这个问题哦,我没有得到赏金?很高兴能帮上忙,但我没有这个。为什么我要释放b
上的锁?由于b
总是由a
调用,我不能只锁定a
的入口,并在调用put b
后解锁吗?a
创建多个线程。但是由于$mutex
是一个全局变量,因此无论b
是否解锁,由a
创建的线程都不会锁定。
$mutex.flock(File::LOCK_EX)