3

次の Ruby コードが機能しないのはなぜですか?

 2 | require 'thread'
 3 | 
 4 | $mutex = Mutex.new
 5 | $mutex.lock
 6 |
 7 | t = Thread.new {
 8 |   sleep 10
 9 |   $mutex.unlock
10 | }
11 | 
12 | $mutex.lock
13 | puts "Delayed hello"

実行すると、エラーが発生します。

./test.rb:13:in `lock': thread 0x7f4557856378 tried to join itself (ThreadError)
    from ./test.rb:13

2 つのスレッドを結合せずに同期する正しい方法は何ですか (同期後も両方のスレッドを実行し続ける必要があります)。

4

2 に答える 2

10

これは古いですが、(執筆時点で)他の答えがどれも正しくないように見えるのは少し怖いので、私は貢献しています。元のコードは明らかに次のことを試みています。

  • メイン スレッドでミューテックスを作成し、ロックします。
  • 新しいスレッドを開始します。このスレッドは、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 バージョンで機能すると仮定するのが妥当です。

于 2014-12-15T04:16:18.590 に答える
-1

12 行目でミューテックスを再度呼び出す必要はありません。

require 'thread'

$mutex = Mutex.new
$mutex.lock
t = Thread.new {
  sleep 10
  $mutex.unlock
}

puts "Delayed hello"

これは機能します。

于 2014-10-22T11:04:14.133 に答える