Jos*_*ber 39 ruby multithreading metaprogramming thread-safety ruby-1.9.2
在Ruby中对类变量执行写入/读取不是线程安全的.对实例变量执行写入/读取似乎是线程安全的.也就是说,对类或元类对象的实例变量执行写/读是否是线程安全的?
在线程安全方面,这三个(人为的)示例之间有什么区别?
例1: 相互排斥
class BestUser # (singleton class)
@@instance_lock = Mutex.new
# Memoize instance
def self.instance
@@instance_lock.synchronize do
@@instance ||= best
end
end
end
Run Code Online (Sandbox Code Playgroud)
例2: 实例变量存储
class BestUser # (singleton class)
# Memoize instance
def self.instance
@instance ||= best
end
end
Run Code Online (Sandbox Code Playgroud)
例3: 在METACLASS上安装可变存储器
class BestUser # (singleton class)
# Memoize instance
class << self
def instance
@instance ||= best
end
end
end
Run Code Online (Sandbox Code Playgroud)
Mat*_*ira 20
例2和3完全相同.模块和类也是对象,在对象上定义单例方法实际上是在其单例类上定义它.
话虽如此,并且由于您已经建立了实例变量访问是线程安全的,示例2和3是线程安全的.示例1也应该是线程安全的,但它不如其他两个,因为它需要手动变量同步.
但是,如果需要利用继承树中共享类变量的事实,则可能必须使用第一种方法.
Ruby语言的固有线程安全性取决于实现.
在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
Run Code Online (Sandbox Code Playgroud)
值x
是始终在MRI一致.然而,在JRuby上,图片发生了变化.相同算法的多个执行产生的值76
,87
,98
,88
,94
.结果可能是任何因为JRuby使用Java线程,它们是真正的线程并且并行执行.
就像在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
Run Code Online (Sandbox Code Playgroud)
Mag*_*gne 11
实例变量不是线程安全的(并且类变量的线程安全性更低)
示例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
Run Code Online (Sandbox Code Playgroud)
因为实例变量在所有线程中共享,例如@VincentXie在他的评论中说明.
PS:实例变量有时被称为"类实例变量",具体取决于它们的使用环境:
当self是一个类时,它们是类的实例变量(类实例变量).当self是一个对象时,它们是对象的实例变量(实例变量).- WindorC对此问题的回答
例2和3完全相同.它们根本不是线程安全的.
请参阅下面的示例.
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)
Run Code Online (Sandbox Code Playgroud)
结果不一样.使用下面的互斥锁时结果相同.
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)
Run Code Online (Sandbox Code Playgroud)
它在CRuby 2.3.0上运行.