访问 ruby 数组线程安全吗?
Is access to ruby Array thread-safe?
假设我有 N 个线程访问一个包含 N 个元素的数组。该数组在线程启动之前就已准备就绪。每个线程会访问不同的元素(线程I会访问元素I,读写都一样)。
理论上,我希望这样的访问模式不会导致任何竞争条件,但 Ruby 在这种情况下真的能保证线程安全吗?
but will Ruby actually guarantee thread safety in this case
Ruby 没有定义的内存模型,因此没有任何类型的保证。
YARV 有一个 Giant VM Lock 可以防止多个 Ruby 线程同时 运行ning,这给了一些 implicit 保证,但这是 YARV 私有的内部实现细节。例如,TruffleRuby、JRuby 和 Rubinius 可以 运行 多个 Ruby 线程并行。
由于没有规范行为应该是什么,任何 Ruby 实现都可以自由地做他们想做的事。最常见的是,Ruby 实现者试图模仿 YARV 的行为,但即便如此也没有明确定义。在 YARV 中,数据结构一般都不是线程安全的,所以如果你想模仿 YARV 的行为,你是否让你所有的数据结构都不是线程安全的?但是在YARV中,多个线程也不能同时运行,所以在很多情况下,操作是隐式线程安全的,所以如果你想模仿YARV,你应该让你的数据结构线程安全吗?
或者,为了模仿 YARV,是否应该防止多个线程同时 运行ning?但是,能够运行多个线程并行实际上是人们选择的原因之一,例如JRuby over YARV。
如您所见,这不是一个微不足道的问题。
最好的解决方案是分别验证每个 Ruby 实现的行为。实际上,这是 第二好的 解决方案。
最好的解决方案是使用类似 concurrent-ruby Gem 的东西,其中 其他人 已经完成了验证每个 Ruby 实现的行为的工作你。 concurrent-ruby 维护者与多个 Ruby 实现有着密切的关系(Chris Seaton,concurrent-ruby 的两位主要维护者之一也是 Truffle[=47= 的主要开发人员],一个 JRuby 核心开发人员,以及 ruby-core 的成员,例如),所以你通常可以确定 concurrent-ruby 中的所有内容在所有支持的 Ruby 实现(当前为 YARV、JRuby 和 TruffleRuby)。
并发 Ruby 有一个 Concurrent::Array
class 是线程安全的。你可以在这里看到它是如何实现的:https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent-ruby/concurrent/array.rb 可以看到,对于YARV,Concurrent::Array
其实和::Array
是一样的,但是对于其他的实现,还需要做更多的工作。
concurrent-ruby 开发人员也在努力指定 Ruby 的内存模型,以便在未来,程序员都知道期望什么和不期望什么,实现者也知道他们可以优化什么,不能优化什么。
可变数组的替代方法
在标准 Ruby 实现中,数组 不是 线程安全的。然而,一个 Queue 是。另一方面,Queue 不完全是 Array,因此您没有 Queue 上所有您可能正在寻找的方法。
Concurrent Ruby gem 提供线程安全的数组 class,但通常线程安全的 classes 会比那些不安全的数组慢.根据您的数据,这可能无关紧要,但这肯定是设计考虑因素。
如果您从一开始就知道您将严重依赖线程,您应该在 Ruby 实现上构建您的应用程序,该实现首先提供并发和线程(例如考虑 JRuby 或 TruffleRuby),并设计您的应用程序以利用 Ractors 或使用其他将数据视为不可变而不是在线程之间共享对象的并发模型。
不可变数据是比共享对象更好的线程模式。如果足够小心,您可能会或可能不会遇到任何给定可变对象的问题,但 Ractors and fiber-local variables 应该比尝试使可变对象免受威胁更快、更安全。不过 YMMV。
假设我有 N 个线程访问一个包含 N 个元素的数组。该数组在线程启动之前就已准备就绪。每个线程会访问不同的元素(线程I会访问元素I,读写都一样)。
理论上,我希望这样的访问模式不会导致任何竞争条件,但 Ruby 在这种情况下真的能保证线程安全吗?
but will Ruby actually guarantee thread safety in this case
Ruby 没有定义的内存模型,因此没有任何类型的保证。
YARV 有一个 Giant VM Lock 可以防止多个 Ruby 线程同时 运行ning,这给了一些 implicit 保证,但这是 YARV 私有的内部实现细节。例如,TruffleRuby、JRuby 和 Rubinius 可以 运行 多个 Ruby 线程并行。
由于没有规范行为应该是什么,任何 Ruby 实现都可以自由地做他们想做的事。最常见的是,Ruby 实现者试图模仿 YARV 的行为,但即便如此也没有明确定义。在 YARV 中,数据结构一般都不是线程安全的,所以如果你想模仿 YARV 的行为,你是否让你所有的数据结构都不是线程安全的?但是在YARV中,多个线程也不能同时运行,所以在很多情况下,操作是隐式线程安全的,所以如果你想模仿YARV,你应该让你的数据结构线程安全吗?
或者,为了模仿 YARV,是否应该防止多个线程同时 运行ning?但是,能够运行多个线程并行实际上是人们选择的原因之一,例如JRuby over YARV。
如您所见,这不是一个微不足道的问题。
最好的解决方案是分别验证每个 Ruby 实现的行为。实际上,这是 第二好的 解决方案。
最好的解决方案是使用类似 concurrent-ruby Gem 的东西,其中 其他人 已经完成了验证每个 Ruby 实现的行为的工作你。 concurrent-ruby 维护者与多个 Ruby 实现有着密切的关系(Chris Seaton,concurrent-ruby 的两位主要维护者之一也是 Truffle[=47= 的主要开发人员],一个 JRuby 核心开发人员,以及 ruby-core 的成员,例如),所以你通常可以确定 concurrent-ruby 中的所有内容在所有支持的 Ruby 实现(当前为 YARV、JRuby 和 TruffleRuby)。
并发 Ruby 有一个 Concurrent::Array
class 是线程安全的。你可以在这里看到它是如何实现的:https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent-ruby/concurrent/array.rb 可以看到,对于YARV,Concurrent::Array
其实和::Array
是一样的,但是对于其他的实现,还需要做更多的工作。
concurrent-ruby 开发人员也在努力指定 Ruby 的内存模型,以便在未来,程序员都知道期望什么和不期望什么,实现者也知道他们可以优化什么,不能优化什么。
可变数组的替代方法
在标准 Ruby 实现中,数组 不是 线程安全的。然而,一个 Queue 是。另一方面,Queue 不完全是 Array,因此您没有 Queue 上所有您可能正在寻找的方法。
Concurrent Ruby gem 提供线程安全的数组 class,但通常线程安全的 classes 会比那些不安全的数组慢.根据您的数据,这可能无关紧要,但这肯定是设计考虑因素。
如果您从一开始就知道您将严重依赖线程,您应该在 Ruby 实现上构建您的应用程序,该实现首先提供并发和线程(例如考虑 JRuby 或 TruffleRuby),并设计您的应用程序以利用 Ractors 或使用其他将数据视为不可变而不是在线程之间共享对象的并发模型。
不可变数据是比共享对象更好的线程模式。如果足够小心,您可能会或可能不会遇到任何给定可变对象的问题,但 Ractors and fiber-local variables 应该比尝试使可变对象免受威胁更快、更安全。不过 YMMV。