为什么Ruby中的数组操作不是原子的?

Sła*_*osz 14 ruby multithreading atomic thread-safety atomicity

在Ruby中,如果array被许多线程修改,则此代码不是线程安全的:

array = []
array << :foo # many threads can run this code
Run Code Online (Sandbox Code Playgroud)

为什么<<操作不是线程安全的?

Gri*_*han 9

array当你应用类似的操作时<<,它是你的程序变量.它分三步进行:

  • 首先将变量复制到CPU寄存器中.
  • CPU执行计算.
  • CPU将结果写回变量存储器.

因此,这种高级单一操作分三步执行.在这些步骤之间,由于线程上下文切换,其他线程可能读取变量的相同(旧)值.这就是为什么它不是原子操作.

  • 您可能想阅读:[**Ruby 中的原子操作**](http://moonbase.rydia.net/mental/blog/programming/atomic-operations-in-ruby.html) (2认同)
  • 没关系.不用了,谢谢.这是我们如何回馈社区的一部分. (2认同)

Cas*_*per 9

实际上使用MRI(Matz的Ruby实现)GIL(全局解释器锁)使任何纯C函数原子化.

由于Array#<<在MRI中实现为纯C代码,因此该操作将是原子的.但请注意,这仅适用于MRI.在JRuby上,情况并非如此.

为了完全理解发生了什么,我建议你阅读这两篇文章,这些文章很好地解释了一切:

没有人理解GIL
没有人理解GIL - 第2部分


the*_*Man 8

如果有多个线程访问同一个数组,请使用Ruby的内置Queue类.它很好地处理生产者和消费者.

这是文档中的示例:

require 'thread'

queue = Queue.new

producer = Thread.new do
  5.times do |i|
    sleep rand(i) # simulate expense
    queue << i
    puts "#{i} produced"
  end
end

consumer = Thread.new do
  5.times do |i|
    value = queue.pop
    sleep rand(i/2) # simulate expense
    puts "consumed #{value}"
  end
end

consumer.join
Run Code Online (Sandbox Code Playgroud)