在JRuby中使用|| =和+ =(jruby-lint警告)

Joh*_*hir 4 ruby jruby thread-safety

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

非本地运营商分配不保证是原子的

哪个代码看起来像这样:

def foo
  @foo ||= Foo.new
end
Run Code Online (Sandbox Code Playgroud)

或这个:

config.assets.precompile += %w( email/email.css )
Run Code Online (Sandbox Code Playgroud)

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

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

Mat*_*ers 7

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

foo ||= 'bar'
Run Code Online (Sandbox Code Playgroud)

内部实际执行的内容如下:

1. unless foo
2.   foo = 'bar'
3. end
Run Code Online (Sandbox Code Playgroud)

因为第1行和第2行是单独评估的,所以在多线程应用程序中可以通过这两者之间的不同线程来更改状态,例如:

thread 1: foo ||= 'bar'
thread 2: foo ||= 'baz'
Run Code Online (Sandbox Code Playgroud)

其执行如下:

# 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' 
# ...
Run Code Online (Sandbox Code Playgroud)

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

thread 1: x += 1
thread 2: x += 1
Run Code Online (Sandbox Code Playgroud)

将执行如下:

# 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
Run Code Online (Sandbox Code Playgroud)

因此x在两次操作之后应该是2,但实际上只增加一次.

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

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

另外,原子宝石可以使用JRuby被用来确保检查和更新操作的原子.我不确定它是否支持数组.

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

希望这可以帮助!