4

当我在我的(Rails)应用程序上运行jruby-lint时,我得到了其中的几个:

非本地运算符分配不保证是原子的

哪个指向看起来像这样的代码:

def foo
  @foo ||= Foo.new
end

或这个:

config.assets.precompile += %w( email/email.css )

其中一些在 app/ 中,其中一些在 config/ 中。我猜这个警告只与左边的东西是一个数组的情况有关,要修复它我应该使用Threadsafe::Array?

我需要更改哪些类型的东西?

4

1 回答 1

7

在 jruby 中,复合表达式 like||=不是原子执行的。当你写:

foo ||= 'bar'

内部实际执行的内容类似于:

1. unless foo
2.   foo = 'bar'
3. end

因为第 1 行和第 2 行是分开评估的,所以在多线程应用程序中,状态可能会被这两者之间的不同线程更改,例如:

thread 1: foo ||= 'bar'
thread 2: foo ||= 'baz'

执行如下:

# foo has not been set yet
1. thread 1: unless foo
2. thread 2: unless foo
3. thread 1:   foo = 'bar'
4. thread 2:   foo = 'baz' 
# ...

请注意, foo 最终会被第二个线程重新分配给“baz”,即使它已经有一个值。使用+=同样有问题,因为:

thread 1: x += 1
thread 2: x += 1

会这样执行:

# x starting value of 0
1. thread1: tempvar = x + 1 # 1
2. thread2: tempvar = x + 1 # 1
3. thread1: x = tempvar     # 1
4. thread2: x = tempvar     # 1

所以x在两次操作之后应该是 2 但实际上只增加了一次。

如果您在 jruby 中运行单线程应用程序/脚本,这都不是问题。如果您要使用多个线程运行,那么如果这些操作用于由多个线程访问的变量,则这些操作在这些线程的执行环境中使用是不安全的。

在线程安全很重要的环境中,您可以通过将操作包装在互斥锁中或使用确保特定操作的线程安全的原语来解决此问题。

atomic gem也可以与 jRuby 一起使用,以确保检查和更新操作的原子性。我不确定它是否支持数组。

有关在 jRuby 中管理并发的更多信息

希望这可以帮助!

于 2013-11-12T21:48:35.057 回答