2014-05-15 76 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