- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
为什么下面的 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
同步两个线程而不加入它们的正确方法是什么(同步后两个线程必须继续运行)?
最佳答案
这是旧的,但我正在贡献,因为其他答案(在撰写本文时)似乎都不是正确的,这有点可怕。原始代码显然试图:
@user2413915:您的解决方案省略了在主线程中再次锁定的步骤,因此它不会按预期等待生成的线程。
@Paul Rubel:您的代码假定生成的线程在主线程之前达到其对互斥体的锁定。这是一个竞争条件。如果主线程继续执行并首先锁定,生成的线程将被阻塞,直到 主线程打印“Delayed hello”,这与期望的结果完全相反。您可能通过粘贴到 IRB 提示符来运行它;如果您尝试修改您的示例,以便 end
和 Mutex 锁在同一行,它将失败,过早打印消息(即“end; $mutex.lock
”)。无论哪种方式,它都依赖于随机运行的 Ruby 运行时的行为。
原始代码原则上实际上应该可以正常工作,尽管可以说缺乏优雅 - 实际上 Ruby 1.9+ 运行时不允许它,因为它“看到”主线程中的两个连续锁而没有解锁并且没有“意识到”有一个生成的线程将进行解锁。 Ruby(在这种情况下在技术上是错误的)引发 ThreadError 死锁异常。
相反,巧妙地使用 ruby 队列。当您尝试从队列中提取内容时,调用将阻塞,直到有可用的项为止。所以:
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"
如果生成的线程首先运行并插入队列,那么主线程将只弹出项目并继续前进。如果主线程试图在派生线程推送之前弹出,它将等待派生线程。
[编辑:请注意,插入队列的对象可能是派生线程处理任务的结果,因此主线程等待处理完成并一次性获得处理结果]。
我已经通过 rbenv
在 Ruby 1.8.7-p375 和 Ruby 2.1.2 上成功地测试了它,因此可以合理地假设标准库 Queue 类在所有常见的主要 Ruby 中都起作用版本。
关于Ruby 线程和互斥量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13830072/
我是一名优秀的程序员,十分优秀!