论坛首页 编程语言技术论坛

Ruby多线程

浏览 10475 次
锁定老帖子 主题:Ruby多线程
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-10-16   最后修改:2010-10-16
使用monitor实现多线程的简单程序如下:
require 'monitor'
class Counter
   attr_reader :count
   def initialize
     @count = 0
     super
   end
   def tick3
     @count += 1
   end
 end
 class Counter2 < Monitor
   attr_reader :count
   def initialize
     @count = 0
     super
   end
   def tick5
     synchronize do
       @count += 1
     end
   end
 end
 c = Counter.new
 t1 = Thread.new { 10000.times { c.tick3 } }
 t2 = Thread.new { 10000.times { c.tick3 } }
 t1.join
 t2.join
 puts c.count
 c = Counter2.new
 t1 = Thread.new { 10000.times { c.tick5 } }
 t2 = Thread.new { 10000.times { c.tick5 } }
 t1.join
 t2.join
 puts c.count

运行了很多次,输出的结果永远是:
13889
20000
本人刚学RUBY,请高手分析一下为何未同步的结果永远是13889,而不会有一点改变。
   发表时间:2010-10-18  
放两天了,居然没有回复,看来得自己动手了,郁闷...
0 请登录后投票
   发表时间:2010-10-19  
大概是因为你在用Ruby 1.8.x吧…?抱歉详细的分析暂时没空写,不过你可以试试换台机器用Ruby 1.8.x来跑会发现不同步的时候数字还是每次跑都一样。然后再试试用Ruby 1.9.x来看就会发现多数时候同步与否都一样了…
0 请登录后投票
   发表时间:2010-10-20  
RednaxelaFX 写道
大概是因为你在用Ruby 1.8.x吧…?抱歉详细的分析暂时没空写,不过你可以试试换台机器用Ruby 1.8.x来跑会发现不同步的时候数字还是每次跑都一样。然后再试试用Ruby 1.9.x来看就会发现多数时候同步与否都一样了…


我用的是1.8.7
0 请登录后投票
   发表时间:2010-10-22   最后修改:2010-10-22
这是竞争条件的问题。
在两个线程抢先式运作(没有同步)的时候,第一个线程 x 执行到 += 内部(实际上是 先执行 Fixnum 的 `+' 方法,然后解释器进行 `=' 赋值)时,可能会出现刚好时间片到期的情况。此时线程调度器把执行权交给第二个线程 y,y 执行完一段时间的 @count 的递增,时间片到期,执行权回到 x。由于 x 的 += 之前已经进行到一半,要么是 `+' 的运算元刚作为实际参数传递进来,要么是 `+' 已经返回而解释器正要进行 `=' 赋值,而无论是哪种情况,它进行的运算都是“过期”了的。
第一种情况下,传递进来的参数(运算元)是上一次 x 执行时的 @count 的值,这时它会把这个过期的数值 +1 并返回给调用者,反而覆盖了 y 执行时递加的 @count 的值,丢失了那部分数据;第二种情况下,`+' 的返回值是上一次 x 执行时 (@count+1) 的值,而在 y 执行期间 @count 早已被递加数次了,线程 x 把 @count+1 赋给 @count,无疑也丢失了 y 执行时的 @count 数据。
同步之后,两个线程不再盲目抢先,每次必然要等队友 `+=' 结束后才执行,自然也就没有这个问题了。
0 请登录后投票
   发表时间:2010-10-22  
苏小脉 写道
这是竞争条件的问题。
在两个线程抢先式运作(没有同步)的时候,第一个线程 x 执行到 += 内部(实际上是 先执行 Fixnum 的 `+' 方法,然后解释器进行 `=' 赋值)时,可能会出现刚好时间片到期的情况。此时线程调度器把执行权交给第二个线程 y,y 执行完一段时间的 @count 的递增,时间片到期,执行权回到 x。由于 x 的 += 之前已经进行到一半,要么是 `+' 的运算元刚作为实际参数传递进来,要么是 `+' 已经返回而解释器正要进行 `=' 赋值,而无论是哪种情况,它进行的运算都是“过期”了的。
第一种情况下,传递进来的参数(运算元)是上一次 x 执行时的 @count 的值,这时它会把这个过期的数值 +1 并返回给调用者,反而覆盖了 y 执行时递加的 @count 的值,丢失了那部分数据;第二种情况下,`+' 的返回值是上一次 x 执行时 (@count+1) 的值,而在 y 执行期间 @count 早已被递加数次了,线程 x 把 @count+1 赋给 @count,无疑也丢失了 y 执行时的 @count 数据。
同步之后,两个线程不再盲目抢先,每次必然要等队友 `+=' 结束后才执行,自然也就没有这个问题了。



首先谢谢你的回答,但可惜你答非所问,请再看一次问题,如果有空,再答一次
0 请登录后投票
   发表时间:2010-10-22  
我运行的结果永远是20000,20000,ruby 1.9.2p0
0 请登录后投票
   发表时间:2010-10-23   最后修改:2010-10-23
Java.Eye管理員 写道
我运行的结果永远是20000,20000,ruby 1.9.2p0

RednaxelaFX 也说了1.9.x是这种结果,只是不知道Ruby的线程调度的细节是怎么的,如果1.9以后是这样的话,那同步是不是多余了?另外,rails最新版本是不是线程安全了,如果是,那么有哪些支持rails应用多线程的部署方案呢(假设应用本身也线程安全)?之前看过robbin关于rails应用的部署方案应该都是单线程运行的
0 请登录后投票
   发表时间:2010-10-23   最后修改:2010-10-23
Java.Eye管理員 写道
我运行的结果永远是20000,20000,ruby 1.9.2p0


你在楼主这段代码前面加一段:
class Fixnum
	alias old_plus +
	def +(operand)
		200.times {}
		old_plus(operand)
	end
end

就会发现 Counter1 和 Counter2 的结果不同了。
0 请登录后投票
   发表时间:2010-10-24  
RednaxelaFX 写道
大概是因为你在用Ruby 1.8.x吧…?抱歉详细的分析暂时没空写,不过你可以试试换台机器用Ruby 1.8.x来跑会发现不同步的时候数字还是每次跑都一样。然后再试试用Ruby 1.9.x来看就会发现多数时候同步与否都一样了…


这个应该是ruby vm对多线程调度的实现区别吧,高手同学有空分析一下?我对详细情况比较感兴趣
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics