繁体   English   中英

默认情况下,Ruby Thread-Safe是否安全?

[英]Is Ruby Thread-Safe by default?

我在控制台中运行了10次以上的代码,结果为100000

i = 0
1000.times do
Thread.start { 100.times { i += 1 } }
end
i

它不应该给我不同的输出,因为我正在使用多个线程读取和更新i 这让我感到疑惑,Ruby默认是否真的是线程安全的? 如果不是,那么为什么我总是看到相同的输出?

ps如果您说的是,默认情况下它不是线程安全的,那么您可以分享一个简单的示例,当我在Rails控制台中运行时,它将给我带来不同的结果吗?

编辑:

换句话说,以上代码是否同时运行1000个线程? 如果是,则结果不应为100000 ALWAYS 如果否,那么如何同时运行多个线程?

如果我加puts ,然后打印顺序i会改变。 这意味着线程相互交错,但是它们是否同时运行?

我不是在问如何使此线程安全。 我了解mutex / locking和同步/异步过程的概念。 因为我了解它们,所以我无法理解此代码的输出。

没有代码是自动线程安全的,您必须努力使其成为线程安全的。

特别是+=操作实际上是三个操作:读取,递增,写入。 如果将这些与其他线程混在一起,则可能会出现不可预测的行为。

考虑两个线程上的以下一系列事件:

  A       B
-------------
READ
         READ
INCR
         INCR
WRITE
         WRITE

这是最简单的情况,您将有两个增量操作,因为它们都使用相同的原始值,其中一个被无效。

在我的测试中,这不太可能在双核系统上发生,但实际上在四核计算机上始终是一个问题,仅因为许多计算机的行为就像两个松散连接的双核系统,每个系统都有自己的缓存。 当使用JRuby时 ,线程支持要好得多,这一点甚至更加明显。 您拥有的示例代码会为我提供随机答案,范围从98200到99500。

为了使该线程安全,您必须使用Mutex或使用Concurrent Ruby之类的库中的原子增量操作,这将为您提供安全执行此操作的工具。

另一种选择是避免在线程之间混合数据或使用诸如Queue类的结构来管理通信。 没有互斥锁,任何两个线程都不能操纵同一对象。

!!! 最终,我找到了一种方法来证明,它不会在irb上始终产生100000

运行以下代码给了我这个主意,

100.times do
i = 0
1000.times do
Thread.start { 100.times { i += 1 } }
end
puts i
end

在大多数情况下,我看到不同的价值观。 通常,它的范围是91k to 100000

在计算机科学中,执行线程是可以由操作系统调度程序独立管理的编程指令的最小序列。线程是轻量级进程。

irb(main):001:0> def calculate_sum(arr)
irb(main):002:1>   sleep(2)
irb(main):003:1>   sum = 0
irb(main):004:1>   arr.each do |item|
irb(main):005:2*     sum += item
irb(main):006:2>   end
irb(main):007:1>   sum
irb(main):008:1> end
=> :calculate_sum
irb(main):009:0>
irb(main):010:0* @items1 = [12, 34, 55]
=> [12, 34, 55]
irb(main):011:0> @items2 = [45, 90, 2]
=> [45, 90, 2]
irb(main):012:0> @items3 = [99, 22, 31]
=> [99, 22, 31]
irb(main):013:0>
irb(main):014:0* threads = (1..3).map do |i|
irb(main):015:1*   Thread.new(i) do |i|
irb(main):016:2*     items = instance_variable_get("@items#{i}")
irb(main):017:2>     puts "items#{i} = #{calculate_sum(items)}"
irb(main):018:2>   end
irb(main):019:1> end
=> [#<Thread:0x2158ab8@(irb):15 run>, #<Thread:0x2158860@(irb):15 run>, #<Thread:0x2158488@(irb):15 run>]
irb(main):020:0> threads.each {|t| t.join}
items3 = 152
items2 = 137
items1 = 101
=> [#<Thread:0x2158ab8@(irb):15 dead>, #<Thread:0x2158860@(irb):15 dead>, #<Thread:0x2158488@(irb):15 dead>]
irb(main):021:0>

这是在Ruby中线程化进程的基本示例。 您具有将数组作为参数@item1, @item2, @item3的主要方法calculate_sum 从那里创建三个线程, threads = (1..3)将它们映射到自己的变量.map do |i| 并使用线程被映射到的变量Thread.start(i)启动一个新的Thread实例。

在这里,您创建一个项目变量,该变量等于实例变量为items = instance_variable_get(<object>)任何items = instance_variable_get(<object>)输出计算结果,并puts "items#{<thread-variable>} = #{calculate_sum(items)}"

如您所见,线程开始同时运行=> [#<Thread:0x2158ab8@(irb):15 run>, #<Thread:0x2158860@(irb):15 run>, #<Thread:0x2158488@(irb):15 run>] 所有线程都是通过调用每个线程并加入它们的线程来执行的。每个threads.each {|t| t,join} threads.each {|t| t,join}

最后一部分是最重要的,所有线程都同时运行和死亡,但是,如果一个线程的进程很长,则该线程必须在程序结束之前结束。 例:

irb(main):023:0> Thread.new do
irb(main):024:1*   puts t
irb(main):025:1>   Thread.new do
irb(main):026:2*     sleep(5)
irb(main):027:2>     puts h
irb(main):028:2>   end
irb(main):029:1> end
=> #<Thread:0x2d070f8@(irb):23 run>
irb(main):030:0> hello
goodbye

第二个线程永远不会退出,因此它将继续运行该进程,直到您削减执行为止。

在主要示例中,结尾有=> [#<Thread:0x2158ab8@(irb):15 dead>, #<Thread:0x2158860@(irb):15 dead>, #<Thread:0x2158488@(irb):15 dead>]因为所有线程都完成了该过程,并立即退出。 为了完成我的过程,您必须为第二个线程提供一个exit

我希望这回答了你的问题。

不幸的是,Ruby没有像Java自Java 5或C ++自C ++ 11以来的正式指定的内存模型。

事实上,红宝石真的没有在所有的官方规范,虽然已经有它多次尝试,他们都有同样的问题,那红宝石的设计师们实际上并不使用它们。 因此,Ruby唯一的规范基本上是“ YARV所做的一切”。 (例如,ISO Ruby语言规范根本没有指定Thread类,因此完全避开了该问题。)

但!!! 对于并发来说,这基本上是不可用的,因为YARV无法并行运行线程,因此在YARV中根本不会出现很多并发问题,因此核心库无法防止这些问题! 但是,如果我们要说Ruby的并发语义是YARV所做的,那么现在的问题就变成了:我们不能在语义中包含并行性这一事实吗? 核心库不是语义的保护部分吗?

像JRuby,Rubinius,IronRuby,MacRuby等实现具有可并行运行的线程,这是一个艰巨的任务。 他们仍在努力找出答案。

因此,TL;医生回答你的问题是:我们不知道是否红宝石是线程安全的,因为我们不知道什么是Ruby的线程语义。

例如,在YARV上运行良好的多线程程序在JRuby上中断是很常见的,但是还是程序的错误还是JRuby的错误? 我们不能说,因为我们没有一个规范可以告诉我们Ruby实现的多线程行为。 我们可以轻松地说,好吧,Ruby是YARV所做的,并且当程序在YARV上运行时,我们必须更改JRuby,以便程序也可以在YARV上运行。 但是,并行性实际上是人们首先选择JRuby的主要原因之一,因此这根本不可行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM