2014-05-15 74 views
3

在这个例子中,我正在寻找同步两个puts,输出将是ababab...,输出上没有任何双重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)} 
+0

@sawa看看这个问题 – fotanus

+0

唉,我没有得到赏金?不过,很高兴能有所帮助。 – Kache

回答

2

简短回答
您对互斥量的使用不正确。使用Queue,您可以填充一个线程,然后使用另一个线程填充pop,但不能使用一个线程锁定Mutex,然后使用另一个线程解锁。

正如@matt解释的那样,有一些微妙的事情发生,比如互斥锁自动解锁以及您看不到的无声异常。

如何互斥常用
互斥用于访问特定的共享资源,就像一个变量或文件。变量和文件的同步因此允许多个线程同步。互斥体并不真正同步线程。

例如:

  1. thread_athread_b可以经由共享布尔变量如true_a_false_b同步。
  2. 您每次使用它时都必须访问,测试和切换该布尔变量 - 一个多步骤过程。
  3. 有必要确保这个多步过程以原子方式发生,即不会中断。这是你使用互斥锁的时候。一个无足轻重例子如下:

 

require 'thread' 
Thread.abort_on_exception = true 
true_a_false_b = true 
mutex = Mutex.new 

thread_a = Thread.new do 
    loop do 
    mutex.lock 
    if true_a_false_b 
     puts "a" 
     true_a_false_b = false 
    end 
    mutex.unlock 
    end 
end 

thread_b = Thread.new do 
    loop do 
    mutex.lock 
    if !true_a_false_b 
     puts "b" 
     true_a_false_b = true 
    end 
    mutex.unlock 
    end 

sleep(1) # if in irb/console, yield the "current" thread to thread_a and thread_b 
2

在你的互斥例如,在方法b创建的线程休眠一段时间,打印b然后尝试解锁互斥。这是不合法的,一个线程不能解锁一个互斥体,除非它已经持有锁,并引发ThreadError如果你尝试:

m = Mutex.new 
m.unlock 

结果:

release.rb:2:in `unlock': Attempt to unlock a mutex which is not locked (ThreadError) 
     from release.rb:2:in `<main>' 

你不会看到这个在你的例子中,因为by default Ruby silently ignores exceptions raised in threads other than the main thread。为此,可以使用Thread::abort_on_exception=改变 - 如果添加

Thread.abort_on_exception = true 

到文件的顶部,你会看到类似这样的:

a 
b 
with-mutex.rb:15:in `unlock': Attempt to unlock a mutex which is not locked (ThreadError) 
     from with-mutex.rb:15:in `block in b' 

(您可能会看到不止一个a,但会只有一个b)。

a方法中,创建获取锁的线程,打印a,调用另一个方法(创建一个新线程并立即返回),然后终止。它似乎没有很好的记录,但是当一个线程终止时,它释放它所拥有的任何锁,因此在这种情况下,锁几乎立即被释放,允许其他线程运行。

整体锁没有太大影响。它并不妨碍b线程完全运行,虽然它确实阻止了两个线程同时运行,但只要线程保持退出状态,它就会立即释放。

我想你可能会想到semaphores,虽然Ruby文档说“Mutex implements a simple semaphore”他们是not quite the same

Ruby不提供标准库中的信号量,但它确实提供了condition variables。 (该链接转到较旧的2.0.0文档。默认情况下,在Ruby 2.1 +中需要使用thread标准库,并且此举似乎导致当前文档不可用。另外请注意,Ruby还有一个单独的monitor库(我认为)以更加面向对象的方式添加了相同的功能(互斥锁和条件变量)。)

使用条件变量和互斥锁可以控制线程。 Uri Agassi’s answer显示了一种可能的方式来做到这一点(虽然我认为他的解决方案开始如何竞争)。

如果你看看source for Queue(再次指向2.0.0的链接 - 在最近的版本中线程库已经转换为C,而Ruby版本更容易遵循),你可以看到它的实现与MutexesConditionVariables。当你调用在a线程$queue.pop队列中的例子中,你最终会以同样的方式调用wait on the mutex为乌里阿加西的回答调用他的方法a$cv.wait($mutex)。同样,当您在b线程中致电$queue << true时,您的结尾号码为calling signal on the condition variable的方式与Uri Agassi在其b线程中呼叫的方式相同。

您的文件锁定示例不起作用的主要原因是文件锁定为多个进程提供了相互协调的方式(通常只有一个人试图同时写入文件)并且不会帮助协调线程在一个过程中。您的文件锁定代码的结构与互斥示例类似,所以很可能会遇到同样的问题。

1

与基于文件的版本问题还没有得到妥善解决。 它不起作用的原因是,如果多次调用同一个文件ff.flock(File::LOCK_EX)不会阻止。 这可以用这个简单的顺序程序进行检查:

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) 

不会阻塞。

+0

我没有得到这个。为什么我想释放'b'的锁?由于'b'总是由'a'调用,所以我不能只锁定'a'的入口并在'puts b'被调用后解锁吗? – fotanus

+0

'a'创建多个线程。但是因为'$ mutex'是一个全局变量,'a'创建的线程根本不锁定,不管是否解锁。 – xxa

+0

我明白了。谢谢!! – fotanus