在回圈內的 Thread 物件上應用 #join 會按順序執行它們。
5.times do |x|
Thread.new {
t= rand(1..5) * 0.25
sleep(t)
puts "Thread #{x}: #{t} seconds"
}.join
end
# Output
# Thread 0: 1.25 seconds
# Thread 1: 1.25 seconds
# Thread 2: 0.5 seconds
# Thread 3: 0.75 seconds
# Thread 4: 0.25 seconds
另一方面,將#join 應用于帶有迭代器的 Thread 物件陣列會并發執行它們。為什么?
threads = []
5.times do |x|
threads << Thread.new {
t = rand(1..5) * 0.25
sleep(t)
puts "Thread #{x}: #{t} seconds"
}
end
threads.each(&:join)
# Output
# Thread 1: 0.25 seconds
# Thread 3: 0.5 seconds
# Thread 0: 1.0 seconds
# Thread 4: 1.0 seconds
# Thread 2: 1.25 seconds
uj5u.com熱心網友回復:
這里有幾點需要說明。
當一個執行緒開始時
使用#new、#start、#fork 實體化執行緒會立即啟動該執行緒的代碼。這與主執行緒同時運行。然而,當在一個短腳本中呼叫一個執行緒而不“加入”它時,主執行緒通常在被呼叫執行緒有機會完成之前結束。對于業余程式員來說,它給人一種#join啟動執行緒的錯誤印象。
thread = Thread.new {
puts "Here's a thread"
}
# (No output)
向呼叫主執行緒添加一個短暫的延遲會給被呼叫執行緒一個完成的機會。
thread = Thread.new {
puts "Here's a thread"
}
sleep(2)
# Here's a thread
#join 實際上做了什么
#join 阻塞主執行緒,并且只阻塞呼叫執行緒,直到被呼叫執行緒完成。任何先前呼叫的執行緒不受影響;它們一直在同時運行并繼續這樣做。
原始示例解釋
在第一個示例中,回圈啟動一個執行緒,并立即“加入”它。由于#join 阻塞主執行緒,回圈暫停,直到第一個執行緒完成。然后回圈迭代,啟動第二個執行緒,“加入”它,并再次暫停回圈,直到該執行緒完成。它純粹是順序的,完全否定了執行緒的觀點。
5.times do |x|
Thread.new {
t= rand(1..5) * 0.25
sleep(t)
puts "Thread #{x}: #{t} seconds"
}.join # <--- this #join is the culprit.
end
用戶 Solomon Slow 在原帖中的評論中說得最好。
創建執行緒后立即“加入”執行緒永遠沒有意義。創建執行緒的唯一原因是呼叫者在新執行緒運行時是否要執行其他操作。在您的第二個示例中,呼叫者所做的“其他事情”是,它創建了更多執行緒。
第二個示例正確執行多執行緒。回圈啟動一個執行緒,迭代,啟動下一個執行緒,迭代,等等。因為我們沒有在回圈內部使用#join ,所以主執行緒不斷迭代并啟動所有執行緒。
那么如何在迭代器中使用 #join 不會造成與第一個示例相同的問題?因為這些執行緒已經并發運行了。請記住,#join 只會阻塞主執行緒,直到“joined”執行緒完成。這個被呼叫的執行緒和所有其他被呼叫的執行緒自創建它們的回圈以來一直在運行,它們將繼續運行并獨立于主執行緒和彼此完成。“加入”所有執行緒順序只是告訴主執行緒:
- 在執行緒 1 完成之前不要繼續(但有可能是這個執行緒,以及一些、全部或其他執行緒可能已經完成)。
- 在執行緒 2 完成之前不要繼續(但有可能是這個執行緒,以及一些、全部或沒有剩余執行緒可能已經完成)。
- ...
- 在執行緒 5 完成之前不要繼續(但有可能這個執行緒已經完成,而所有剩余的執行緒肯定已經完成)。
實際上,這最后一行順序地指示主執行緒暫停,但它不會妨礙被呼叫的執行緒。
threads.each(&:join)
我也發現這個解釋非常有幫助。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/380712.html
