これは古いですが、(執筆時点で)他の答えがどれも正しくないように見えるのは少し怖いので、私は貢献しています。元のコードは明らかに次のことを試みています。
- メイン スレッドでミューテックスを作成し、ロックします。
- 新しいスレッドを開始します。このスレッドは、Ruby ランタイムの気まぐれに応じて、いつでも実行を開始できます。
- このスレッドは、その作業が完了してからのみミューテックスのロックを解除してください。
- 次に、メイン スレッドにミューテックスを故意に再ロックさせ、ロックを解除するスレッドを生成することを意図します。メインスレッドはそれを待ちます。
- その後、メイン スレッドは引き続き実行されます。
@ user2413915: あなたのソリューションでは、メイン スレッドで再びロックする手順が省略されているため、生成されたスレッドが意図したとおりに待機しません。
@Paul Rubel: あなたのコードは、生成されたスレッドがメイン スレッドよりも先にミューテックスのロックまで到達することを前提としています。これは競合状態です。メイン スレッドが実行を継続し、最初にロックした場合、生成されたスレッドは、メイン スレッドが「Delayed hello」を出力するまでブロックされます。これは、望ましい結果とは正反対です。おそらく、IRB プロンプトに貼り付けて実行したでしょう。と Mutex ロックが同じ行にあるように例を修正して試してみると、end
失敗し、メッセージの出力が早すぎます (つまり、" end; $mutex.lock
")。いずれにせよ、たまたま動いている Ruby ランタイムの振る舞いに依存しています。
元のコードは、エレガンスに欠けているとはいえ、原則として実際には問題なく動作するはずです。実際には、Ruby 1.9+ ランタイムでは、ロック解除なしでメインスレッドで 2 つの連続したロックを「認識」し、「実現」しないため、許可されません。ロックを解除するために生成されたスレッドがあること。Ruby は (この場合は技術的に誤って) ThreadError デッドロック例外を発生させます。
代わりに、ruby Queue を巧みに利用してください。キューから何かを取得しようとすると、アイテムが利用可能になるまで呼び出しがブロックされます。そう:
require 'thread'
require 'queue'
queue = Queue.new
t = Thread.new {
sleep 10
queue.push( nil ) # Push any object you like - here, it's a NilClass instance
}
queue.pop() # Blocks until thread 't' pushes onto the queue
puts "Delayed hello"
生成されたスレッドが最初に実行され、キューにプッシュされた場合、メイン スレッドはアイテムをポップして続行します。生成されたスレッドがプッシュする前にメイン スレッドがポップしようとすると、生成されたスレッドを待機します。
[編集: キューにプッシュされたオブジェクトは、生成されたスレッドの処理タスクの結果である可能性があるため、メインスレッドは処理が完了するまで待機し、処理結果を一度に取得することに注意してください].
これを Ruby 1.8.7-p375 と Ruby 2.1.2 でテストしrbenv
て成功したので、標準ライブラリの Queue クラスがすべての一般的な主要な Ruby バージョンで機能すると仮定するのが妥当です。