簡體   English   中英

為什么 Ruby 中對數組的 << 操作不是原子操作?

[英]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 上,情況並非如此。

要完全了解發生了什么,我建議您閱讀這兩篇文章,它們很好地解釋了一切:

沒有人了解 GIL
沒有人了解 GIL - 第 2 部分

當您對其應用<<類的操作時, array是您的程序變量。 它分三步進行:

  • 該變量首先被復制到 CPU 寄存器中。
  • CPU 執行計算。
  • CPU 將結果寫回變量存儲器。

所以這個高級單操作分三步進行。 在這些步驟之間,由於線程上下文切換,其他線程可能會讀取變量的相同(舊)值。 這就是為什么它不是原子操作的原因。

如果您有多個線程訪問同一個數組,請使用 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

該鏈接可能對您有幫助:

http://www.jstorimer.com/pages/ruby-core-classes-arent-thread-safe

另外,您可能對此寶石也感興趣:

https://rubygems.org/gems/thread_safe

因為 Ruby 是一種非常高級的語言,所以在操作系統級別沒有什么是真正的原子。 只有非常簡單的匯編操作在 OS 層面是原子的(依賴於 OS),而每一個 Ruby 操作,即使是簡單的1 + 1對應執行成百上千條匯編指令,例如方法查找、垃圾收集、對象初始化、作用域計算等

如果您需要使操作原子化,請使用互斥鎖。

只是從@Linuxios 和@TheTinMan 中取笑:高級語言(HLL)操作通常不是原子的。 原子性(通常)在單線程程序中不是問題。 在多線程程序中,您(程序員)必須以比單個 HLL 操作高得多的粒度對其進行推理,因此具有原子性的單個 HLL 操作實際上對您沒有多大幫助。 另一方面,盡管使 HLL 操作原子化只需前后幾條機器指令——至少在現代硬件上——靜態(二進制大小)和動態(執行時間)開銷加起來。 更糟糕的是,顯式原子性幾乎禁用了所有優化,因為編譯器無法跨原子操作移動指令。 沒有真正的好處 + 顯着的成本 = 非入門者。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM