[英]Why is the << operation on an array in Ruby not atomic?
在 Ruby 中,如果array
被多個線程修改,則此代碼不是線程安全的:
array = []
array << :foo # many threads can run this code
為什么<<
操作不是線程安全的?
實際上使用 MRI(Matz 的 Ruby 實現)GIL(全局解釋器鎖)使任何純 C 函數原子化。
由於Array#<<
在 MRI 中被實現為純 C 代碼,因此該操作將是原子的。 但請注意,這僅適用於 MRI。 在 JRuby 上,情況並非如此。
要完全了解發生了什么,我建議您閱讀這兩篇文章,它們很好地解釋了一切:
當您對其應用<<
類的操作時, array
是您的程序變量。 它分三步進行:
所以這個高級單操作分三步進行。 在這些步驟之間,由於線程上下文切換,其他線程可能會讀取變量的相同(舊)值。 這就是為什么它不是原子操作的原因。
如果您有多個線程訪問同一個數組,請使用 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
因為 Ruby 是一種非常高級的語言,所以在操作系統級別沒有什么是真正的原子。 只有非常簡單的匯編操作在 OS 層面是原子的(依賴於 OS),而每一個 Ruby 操作,即使是簡單的1 + 1
對應執行成百上千條匯編指令,例如方法查找、垃圾收集、對象初始化、作用域計算等
如果您需要使操作原子化,請使用互斥鎖。
只是從@Linuxios 和@TheTinMan 中取笑:高級語言(HLL)操作通常不是原子的。 原子性(通常)在單線程程序中不是問題。 在多線程程序中,您(程序員)必須以比單個 HLL 操作高得多的粒度對其進行推理,因此具有原子性的單個 HLL 操作實際上對您沒有多大幫助。 另一方面,盡管使 HLL 操作原子化只需前后幾條機器指令——至少在現代硬件上——靜態(二進制大小)和動態(執行時間)開銷加起來。 更糟糕的是,顯式原子性幾乎禁用了所有優化,因為編譯器無法跨原子操作移動指令。 沒有真正的好處 + 顯着的成本 = 非入門者。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.