我做了一个快速的谷歌搜索,几乎所有用Ruby编写的原子性建议都围绕操作包装Mutex。但是,我怀疑这种方法不能满足通常的原子性定义,因为信号可能会中断同步代码。例如(取自Ruby Best Practices):
lock = Mutex.new
# XXX this is an example of what NOT to do inside a signal handler:
trap(:USR1) do
lock.synchronize do
# if a second SIGUSR1 arrives here, this block of code
# will fire again. Attempting Mutex#synchronize twice
# the same thread leads to a deadlock error
end
end
我知道原子性对于高级语言来说并不那么重要,但是为了研究起见,我希望得到关于GIL实现的这个问题的规范性答案(例如 MRI 2.0.0 )没有例如 JRuby 1.7.4和Rubinius 1.2.4
答案 0 :(得分:1)
我对这个主题知之甚少。但我会尽力回答。
Jesse Storimer写了一篇关于并发的文章。我强烈建议你阅读有关它的所有3个部分。
第2部分的结论是GIL保证本机C方法实现是原子的。
你提供的例子实际上更多是重入问题然后是原子性。我不知道这是相同的事情还是它有多密切相关。
与文章说明一样,ruby不同于事件驱动编程,其中回调是同步的,这意味着如果你发送信号USR1两次,第二个处理程序将在第一个完成后执行。所以你不会两次锁定互斥锁。
但是在ruby中的信号处理是异步的。意思是如果你发送信号两次。第二个处理程序将中断第一个处理程序。因为第一个已经获得了锁,所以尝试获取相同锁的第二个处理程序将抛出异常。我相信这个问题不是特定于ruby的。
解决此问题的方法之一是创建一个队列来执行信号处理。另一个解决方案是使用一种称为“自管”技巧的方法。这两种方法。在这篇文章中再次讨论了令人敬畏的Jesse Storimer:
http://rubysource.com/the-self-pipe-trick-explained/
所以, 对于MRI 2.0.0,我相信仍然有GIL,所以ruby只保证原生C方法是原子的。
JRuby是JVM支持的所以我的猜测是所有的线程和锁定机制都是在JVM之上实现的
Rubinius 1.2还有GIL,所以我相信它和MRI一样。但是Rubinius 2.x移除了GIL。我对Rubinius没有多少经验,所以我不太确定。
要回答这个问题,如果您正在使用ruby中的多线程应用程序。互斥类应该保护该块一次只能由单个线程执行。