2011-09-30 36 views
4

最近,我一直在使用Ruby的線程,並發現了一些意外的行爲。在關鍵部分中,調用raise會導致互斥體釋放。我可以期待這個synchronize方法和它的區塊,但是當lockunlock被單獨調用時,它似乎也會發生。ruby​​異常如何導致muttle解鎖?

例如,下面的代碼輸出:

$ ruby testmutex.rb 
x sync 
y sync 

...這裏,我期望y被阻塞,直到宇宙熱寂。

m = Mutex.new 


x = Thread.new() do 
    begin 
    m.lock 
     puts "x sync" 
     sleep 5 
     raise "x err" 
     sleep 5 
    m.unlock 
    rescue 
    end 
end 


y = Thread.new() do 
    sleep 0.5 
    m.lock 
    puts "y sync" 
    m.unlock 
end 


x.join 
y.join 

爲什麼y線程允許運行,即使x線程中的m.unlock永遠不會執行?

+0

這是一個有趣的觀察。然而......你的問題是什麼? Ruby內部實現中的「如何實現」?爲什麼會發生?它是打算還是一個錯誤? – Phrogz

+0

我很好奇它是如何實現的,但主要問題是原因,我相信你已經用很多口才解釋過了 - 謝謝。 –

回答

2

請注意,如果您從x中刪除raiseunlock,則其行爲是相同的。所以你有一種情況,x線程鎖定互斥鎖,然後線程結束,並且互斥鎖被解鎖。

m = Mutex.new 
Thread.new{ m.lock; p m.locked? }.join 
#=> true 

p m.locked? 
#=> false 

因此我們看到情況與raise無關。由於您的raise周圍有一個begin/rescue區塊,因此您只需比其他情況提前5秒退出x線程。

據推測,解釋器可以跟蹤線程鎖定的任何互斥鎖,並在線程死亡時自動並有意地解鎖它們。 (但是,我不能用源代碼檢查來支持它,這只是一種猜測,基於行爲)。

+0

有趣。在'rescue'和'end'之間插入一個'sleep'可以很好地說明這一點。 釋放鎖的線程的概念完全在我的預期之內,我想我從未想過ruby會如此聰明。 –

+1

你可以在源代碼中看到它:[thread.c:338](https://github.com/ruby/ruby/blob/trunk/thread.c#L338) –