线程安全:Ruby中的类变量

时间:2022-01-10 20:40:23

Performing writes/reads on class variables in Ruby is not thread safe. Performing writes/reads on instance variables appears to be thread safe. That said, is it thread safe to perform write/reads on instance variables of a class or metaclass object?

在Ruby中对类变量执行写入/读取不是线程安全的。对实例变量执行写入/读取似乎是线程安全的。也就是说,对类或元类对象的实例变量执行写/读是否是线程安全的?

What are the differences between these three (contrived) examples in terms of thread safety?

在线程安全方面,这三个(人为的)示例之间有什么区别?

EXAMPLE 1: MUTUAL EXCLUSION

例1:相互排斥

class BestUser # (singleton class)
  @@instance_lock = Mutex.new

  # Memoize instance
  def self.instance
    @@instance_lock.synchronize do
      @@instance ||= best
    end
  end
end

EXAMPLE 2: INSTANCE VARIABLE STORAGE

例2:实例变量存储

class BestUser # (singleton class)
  # Memoize instance
  def self.instance
    @instance ||= best
  end
end

EXAMPLE 3: INSTANCE VARIABLE STORAGE ON METACLASS

例3:在METACLASS上安装可变存储器

class BestUser # (singleton class)
  # Memoize instance
  class << self
    def instance
      @instance ||= best
    end
  end
end

3 个解决方案

#1


6  

Instance variables are not thread safe (and class variables are even less thread safe)

实例变量不是线程安全的(并且类变量的线程安全性更低)

Example 2 and 3, both with instance variables, are equivalent, and they are NOT thread safe, like @VincentXie stated. However, here is a better example to demonstrate why they are not:

示例2和3都是实例变量,它们是等效的,并且它们不是线程安全的,如@VincentXie所述。但是,这里有一个更好的例子来说明它们为什么不是:

class Foo
  def self.bar(message)
    @bar ||= message
  end
end

t1 = Thread.new do
    puts "bar is #{Foo.bar('thread1')}"
end

t2 = Thread.new do
    puts "bar is #{Foo.bar('thread2')}"
end

sleep 2

t1.join
t2.join

=> bar is thread1
=> bar is thread1

Because the instance variable is shared amongst all of the threads, like @VincentXie stated in his comment.

因为实例变量在所有线程*享,例如@VincentXie在他的评论中说明。

PS: Instance variables are sometimes referred to as "class instance variables", depending on the context in which they are used:

PS:实例变量有时被称为“类实例变量”,具体取决于它们的使用环境:

When self is a class, they are instance variables of classes(class instance variables). When self is a object, they are instance variables of objects(instance variables). - WindorC's answer to a question about this

当self是一个类时,它们是类的实例变量(类实例变量)。当self是一个对象时,它们是对象的实例变量(实例变量)。 - WindorC对此问题的回答

#2


21  

Examples 2 and 3 are exactly the same. Modules and classes are also objects, and defining a singleton method on a object actually defines it on its singleton class.

例2和3完全相同。模块和类也是对象,在对象上定义单例方法实际上是在其单例类上定义它。

With that said, and since you have already established instance variable access is thread safe, examples 2 and 3 are thread safe. Example 1 should also be thread safe, but it is inferior to the other two because it requires manual variable synchronization.

话虽如此,并且由于您已经建立了实例变量访问是线程安全的,示例2和3是线程安全的。示例1也应该是线程安全的,但它不如其他两个,因为它需要手动变量同步。

However, if you need to take advantage of the fact that class variables are shared within the inheritance tree, you may have to use the first approach.

但是,如果需要利用继承树*享类变量的事实,则可能必须使用第一种方法。


The inherent thread safety of the Ruby language depends on the implementation.

Ruby语言的固有线程安全性取决于实现。

MRI, before 1.9, implemented threads at the VM level. This means that even though Ruby is capable of scheduling code execution, nothing is really running in parallel within a single Ruby process. Ruby 1.9 uses native threads synchronized with a global interpreter lock. Only the context which holds the lock may execute code.

在1.9之前的MRI,在VM级别实现了线程。这意味着即使Ruby能够调度代码执行,但在单个Ruby进程中并没有真正并行运行。 Ruby 1.9使用与全局解释器锁同步的本机线程。只有持有锁的上下文才可以执行代码。

n, x = 10, 0

n.times do
  Thread.new do
    n.times do
      x += 1
    end
  end
end

sleep 1
puts x
# 100

The value of x is always consistent on MRI. On JRuby, however, the picture changes. Multiple executions of the same algorithm yielded the values 76, 87, 98, 88, 94. The result could be anything because JRuby uses Java threads, which are real threads and execute in parallel.

x的值在MRI上始终是一致的。然而,在JRuby上,图片发生了变化。多次执行相同的算法产生了值76,87,98,88,94。结果可能是任何因为JRuby使用Java线程,它们是真正的线程并且并行执行。

Just like in the Java language, manual synchronization is required in order to safely use threads in JRuby. The following code always results in consistent values for x:

就像在Java语言中一样,为了在JRuby中安全地使用线程,需要手动同步。以下代码始终为x生成一致的值:

require 'thread'
n, x, mutex = 10, 0, Mutex.new

n.times do
  Thread.new do
    n.times do
      mutex.synchronize do
        x += 1
      end
    end
  end
end

sleep 1
puts x
# 100

#3


6  

Examples 2 and 3 are exactly the same. They are not thread-safety at all.

例2和3完全相同。它们根本不是线程安全的。

Please see the example Below.

请参阅下面的示例。

class Foo
  def self.bar
    @bar ||= create_no
  end

  def self.create_no
    no = rand(10000)
    sleep 1
    no
  end
end

10.times.map do
  Thread.new do
    puts "bar is #{Foo.bar}"
  end
end.each(&:join)

It's result is not same. The result is same when using mutex as below.

结果不一样。使用如下互斥时的结果相同。

class Foo
  @mutex = Mutex.new

  def self.bar
    @mutex.synchronize {
      @bar ||= create_no
    }
  end

  def self.create_no
    no = rand(10000)
    sleep 1
    no
  end
end

10.times.map do
  Thread.new do
    puts "bar is #{Foo.bar}"
  end
end.each(&:join)

It is run on CRuby 2.3.0.

它在CRuby 2.3.0上运行。

#1


6  

Instance variables are not thread safe (and class variables are even less thread safe)

实例变量不是线程安全的(并且类变量的线程安全性更低)

Example 2 and 3, both with instance variables, are equivalent, and they are NOT thread safe, like @VincentXie stated. However, here is a better example to demonstrate why they are not:

示例2和3都是实例变量,它们是等效的,并且它们不是线程安全的,如@VincentXie所述。但是,这里有一个更好的例子来说明它们为什么不是:

class Foo
  def self.bar(message)
    @bar ||= message
  end
end

t1 = Thread.new do
    puts "bar is #{Foo.bar('thread1')}"
end

t2 = Thread.new do
    puts "bar is #{Foo.bar('thread2')}"
end

sleep 2

t1.join
t2.join

=> bar is thread1
=> bar is thread1

Because the instance variable is shared amongst all of the threads, like @VincentXie stated in his comment.

因为实例变量在所有线程*享,例如@VincentXie在他的评论中说明。

PS: Instance variables are sometimes referred to as "class instance variables", depending on the context in which they are used:

PS:实例变量有时被称为“类实例变量”,具体取决于它们的使用环境:

When self is a class, they are instance variables of classes(class instance variables). When self is a object, they are instance variables of objects(instance variables). - WindorC's answer to a question about this

当self是一个类时,它们是类的实例变量(类实例变量)。当self是一个对象时,它们是对象的实例变量(实例变量)。 - WindorC对此问题的回答

#2


21  

Examples 2 and 3 are exactly the same. Modules and classes are also objects, and defining a singleton method on a object actually defines it on its singleton class.

例2和3完全相同。模块和类也是对象,在对象上定义单例方法实际上是在其单例类上定义它。

With that said, and since you have already established instance variable access is thread safe, examples 2 and 3 are thread safe. Example 1 should also be thread safe, but it is inferior to the other two because it requires manual variable synchronization.

话虽如此,并且由于您已经建立了实例变量访问是线程安全的,示例2和3是线程安全的。示例1也应该是线程安全的,但它不如其他两个,因为它需要手动变量同步。

However, if you need to take advantage of the fact that class variables are shared within the inheritance tree, you may have to use the first approach.

但是,如果需要利用继承树*享类变量的事实,则可能必须使用第一种方法。


The inherent thread safety of the Ruby language depends on the implementation.

Ruby语言的固有线程安全性取决于实现。

MRI, before 1.9, implemented threads at the VM level. This means that even though Ruby is capable of scheduling code execution, nothing is really running in parallel within a single Ruby process. Ruby 1.9 uses native threads synchronized with a global interpreter lock. Only the context which holds the lock may execute code.

在1.9之前的MRI,在VM级别实现了线程。这意味着即使Ruby能够调度代码执行,但在单个Ruby进程中并没有真正并行运行。 Ruby 1.9使用与全局解释器锁同步的本机线程。只有持有锁的上下文才可以执行代码。

n, x = 10, 0

n.times do
  Thread.new do
    n.times do
      x += 1
    end
  end
end

sleep 1
puts x
# 100

The value of x is always consistent on MRI. On JRuby, however, the picture changes. Multiple executions of the same algorithm yielded the values 76, 87, 98, 88, 94. The result could be anything because JRuby uses Java threads, which are real threads and execute in parallel.

x的值在MRI上始终是一致的。然而,在JRuby上,图片发生了变化。多次执行相同的算法产生了值76,87,98,88,94。结果可能是任何因为JRuby使用Java线程,它们是真正的线程并且并行执行。

Just like in the Java language, manual synchronization is required in order to safely use threads in JRuby. The following code always results in consistent values for x:

就像在Java语言中一样,为了在JRuby中安全地使用线程,需要手动同步。以下代码始终为x生成一致的值:

require 'thread'
n, x, mutex = 10, 0, Mutex.new

n.times do
  Thread.new do
    n.times do
      mutex.synchronize do
        x += 1
      end
    end
  end
end

sleep 1
puts x
# 100

#3


6  

Examples 2 and 3 are exactly the same. They are not thread-safety at all.

例2和3完全相同。它们根本不是线程安全的。

Please see the example Below.

请参阅下面的示例。

class Foo
  def self.bar
    @bar ||= create_no
  end

  def self.create_no
    no = rand(10000)
    sleep 1
    no
  end
end

10.times.map do
  Thread.new do
    puts "bar is #{Foo.bar}"
  end
end.each(&:join)

It's result is not same. The result is same when using mutex as below.

结果不一样。使用如下互斥时的结果相同。

class Foo
  @mutex = Mutex.new

  def self.bar
    @mutex.synchronize {
      @bar ||= create_no
    }
  end

  def self.create_no
    no = rand(10000)
    sleep 1
    no
  end
end

10.times.map do
  Thread.new do
    puts "bar is #{Foo.bar}"
  end
end.each(&:join)

It is run on CRuby 2.3.0.

它在CRuby 2.3.0上运行。