1

我做了一个快速的谷歌搜索,几乎所有关于 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

4

1 回答 1

1

我对这个话题的了解非常有限。但我会尽量回答。

Jesse Storimer 写了一篇关于并发的非常好的文章。我强烈建议您阅读有关它的所有 3 个部分。

http://www.jstorimer.com/blogs/workingwithcode/8100871-nobody-understands-the-gil-part-2-implementation

第 2 部分的结论是 GIL 保证原生 C 方法实现是原子的。

您给出的示例实际上更多的是重入问题而不是原子性。我不知道这是否是同一件事或它有多密切相关。

就像文章解释的那样,ruby 与回调是同步的事件驱动编程不同,这意味着如果您发送信号 USR1 两次,第二个处理程序将在第一个处理程序完成后执行。所以你不会两次锁定互斥锁。

但是在 ruby​​ 中的信号处理是异步的。这意味着如果您发送信号两次。第二个处理程序将中断第一个处理程序。因为第一个已经获取了锁,那么尝试获取相同锁的第二个处理程序将抛出异​​常。而且我相信这个问题不是红宝石特有的。

解决这个问题的方法之一是创建一个队列来进行信号处理。另一个解决方案是使用一种称为“self-pipe”技巧的方法。两种方法。在这篇文章中再次由令人敬畏的 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​​ 中的多线程应用程序。互斥锁类应该保护该块一次只能由单个线程执行。

于 2013-08-07T21:33:28.133 回答