从理论上讲, 方法可以解决这个问题。根据 Ruby 的源代码文档,Thread.pass
调用线程调度程序将执行权传递给另一个线程。这是 Ruby 文档提供的示例:
根据文档,此代码在运行时会生成以下输出:
axbycz
是的,确实如此。理论上,这似乎表明,通过在每次调用 print
之后调用 Thread.pass
,这些线程将执行权传递给另一个线程,这就是两个线程的输出交替的原因。
出于我心中的疑问,我想知道 Thread.pass
的调用被删除后会产生什么影响?第一个线程是否会一直占用,只有在结束后才让步于第二个线程?找出答案的最佳方法是尝试:
a = Thread.new { print "a";
print "b";
print "c" }
b = Thread.new { print "x";
print "y";
print "z" }
a.join
b.join
事实上,(令我惊讶的是!),实际产生的输出是:
axbycz
换句话说,无论是否调用 Thread.pass
,结果都是相同的。那么,Thread.pass
做什么呢?其宣称 pass
方法,调用线程调度程序将执行权传递给另一个线程,该文档是错误的吗?
在一个短暂而愤怒的时刻,我承认我轻率的认为有一种可能性,文档是不正确的,并且 Thread.pass
根本没有做任何事情。深入研究 Ruby 的 C 语言源代码很快消除了我的疑虑;Thread.pass
确实做了一些事情,但它的行为并不像 Ruby 文档暗示的那样可预测。在解释原因之前,让我们尝试一下我自己的示例:
s = 'start '
a = Thread.new { (1..10).each{
s << 'a'
Thread.pass
}
}
b = Thread.new { (1..10).each{
Thread.pass
}
}
a.join
b.join
puts( "#{s} end" )
乍一看,这可能看起来与前面的示例非常相似。它设置两个线程运行,但不是反复打印东西出来,而是重复地将一个字符附加到字符串中 - ‘a’ 由线程 a
添加,’b’ 由线程 b
添加。每次操作后,Thread.pass
将执行权传递给另一个线程。最后显示整个字符串。字符串包含 ‘a’ 和 ‘b’ 的交替序列应该不足为奇:
s = 'start '
a = Thread.new { (1..10).each{
s << 'a'
}
}
b = Thread.new { (1..10).each{
s << 'b'
}
}
a.join
b.join
puts( "#{s} end" )
这次,输出如下:
换句话说,这个程序显示了我最初在第一个程序中预料的那种不同的行为(我从 Ruby 的嵌入式文档中复制出来的那个)- 也就是说当两个线程在它们自己的时间片下运行时,第一个线程,a
,抢占所有时间为它自己所用,只有当它完成时第二个线程 b
才会得到关注。但是通过显式添加对 的调用,我们可以强制每个线程将执行权传递给任何其它线程。
那么我们如何解释这种行为上的差异呢?从本质上讲,pass0.rb 和 pass3.rb 正在做同样的事情 - 运行两个线程并显示每个线程的字符串。唯一真正的区别在于,在 pass3.rb 中,字符串在线程内连接而不是打印。这可能看起来不是什么大不了的事,但事实证明,打印字符串比连接字符串需要更多的时间。实际上,print
调用会引入时间延迟。正如我们之前发现的那样(当我们有意使用 sleep
引入延迟时),时间延迟对线程产生了深远的影响。
如果你仍然不相信,请尝试我重写的 pass0.rb 版本,我创造性地命名为 pass0_new.rb。这只是用连接替换了打印。现在,如果你对 Thread.pass
的调用进行注释和取消注释,你确实会看到不同的结果。
作为最后一个示例,你可能需要查看 pass4.rb 程序。这会创建两个线程并立即挂起它们(Thread.stop
)。在每个线程的主体中,线程的信息(包括其 object_id
)被附加到数组 arr
,然后调用 Thread.pass
。最后,运行并连接两个线程,并显示数组 arr
。尝试通过取消注释 Thread.pass
来验证其效果(密切注意其 object_id
标识符指示的线程的执行顺序):
arr = []
t1 = Thread.new{
Thread.stop
(1..10).each{
arr << Thread.current.to_s
Thread.pass
}
}
t2 = Thread.new{
Thread.stop
(1..10).each{ |i|
arr << Thread.current.to_s
Thread.pass
}
}
puts( "Starting threads..." )
t1.run
t2.run
t1.join
puts( arr )