从理论上讲, 方法可以解决这个问题。根据 Ruby 的源代码文档,Thread.pass 调用线程调度程序将执行权传递给另一个线程。这是 Ruby 文档提供的示例:

    pass0.rb

    根据文档,此代码在运行时会生成以下输出:

    1. axbycz

    是的,确实如此。理论上,这似乎表明,通过在每次调用 print 之后调用 Thread.pass,这些线程将执行权传递给另一个线程,这就是两个线程的输出交替的原因。

    出于我心中的疑问,我想知道 Thread.pass 的调用被删除后会产生什么影响?第一个线程是否会一直占用,只有在结束后才让步于第二个线程?找出答案的最佳方法是尝试:

    pass1.rb
    1. a = Thread.new { print "a";
    2. print "b";
    3. print "c" }
    4. b = Thread.new { print "x";
    5. print "y";
    6. print "z" }
    7. a.join
    8. b.join

    事实上,(令我惊讶的是!),实际产生的输出是:

    1. axbycz

    换句话说,无论是否调用 Thread.pass,结果都是相同的。那么,Thread.pass 做什么呢?其宣称 pass方法,调用线程调度程序将执行权传递给另一个线程,该文档是错误的吗?

    在一个短暂而愤怒的时刻,我承认我轻率的认为有一种可能性,文档是不正确的,并且 Thread.pass 根本没有做任何事情。深入研究 Ruby 的 C 语言源代码很快消除了我的疑虑;Thread.pass 确实做了一些事情,但它的行为并不像 Ruby 文档暗示的那样可预测。在解释原因之前,让我们尝试一下我自己的示例:

    pass2.rb
    1. s = 'start '
    2. a = Thread.new { (1..10).each{
    3. s << 'a'
    4. Thread.pass
    5. }
    6. }
    7. b = Thread.new { (1..10).each{
    8. Thread.pass
    9. }
    10. }
    11. a.join
    12. b.join
    13. puts( "#{s} end" )

    乍一看,这可能看起来与前面的示例非常相似。它设置两个线程运行,但不是反复打印东西出来,而是重复地将一个字符附加到字符串中 - ‘a’ 由线程 a 添加,’b’ 由线程 b 添加。每次操作后,Thread.pass 将执行权传递给另一个线程。最后显示整个字符串。字符串包含 ‘a’ 和 ‘b’ 的交替序列应该不足为奇:

    pass3.rb
    1. s = 'start '
    2. a = Thread.new { (1..10).each{
    3. s << 'a'
    4. }
    5. }
    6. b = Thread.new { (1..10).each{
    7. s << 'b'
    8. }
    9. }
    10. a.join
    11. b.join
    12. puts( "#{s} end" )

    这次,输出如下:

      换句话说,这个程序显示了我最初在第一个程序中预料的那种不同的行为(我从 Ruby 的嵌入式文档中复制出来的那个)- 也就是说当两个线程在它们自己的时间片下运行时,第一个线程,a,抢占所有时间为它自己所用,只有当它完成时第二个线程 b 才会得到关注。但是通过显式添加对 的调用,我们可以强制每个线程将执行权传递给任何其它线程。

      那么我们如何解释这种行为上的差异呢?从本质上讲,pass0.rbpass3.rb 正在做同样的事情 - 运行两个线程并显示每个线程的字符串。唯一真正的区别在于,在 pass3.rb 中,字符串在线程内连接而不是打印。这可能看起来不是什么大不了的事,但事实证明,打印字符串比连接字符串需要更多的时间。实际上,print 调用会引入时间延迟。正如我们之前发现的那样(当我们有意使用 sleep 引入延迟时),时间延迟对线程产生了深远的影响。

      如果你仍然不相信,请尝试我重写的 pass0.rb 版本,我创造性地命名为 pass0_new.rb。这只是用连接替换了打印。现在,如果你对 Thread.pass 的调用进行注释和取消注释,你确实会看到不同的结果。

      pass0_new.rb

      作为最后一个示例,你可能需要查看 pass4.rb 程序。这会创建两个线程并立即挂起它们(Thread.stop)。在每个线程的主体中,线程的信息(包括其 object_id)被附加到数组 arr,然后调用 Thread.pass。最后,运行并连接两个线程,并显示数组 arr。尝试通过取消注释 Thread.pass 来验证其效果(密切注意其 object_id 标识符指示的线程的执行顺序):

      pass4.rb
      1. arr = []
      2. t1 = Thread.new{
      3. Thread.stop
      4. (1..10).each{
      5. arr << Thread.current.to_s
      6. Thread.pass
      7. }
      8. }
      9. t2 = Thread.new{
      10. Thread.stop
      11. (1..10).each{ |i|
      12. arr << Thread.current.to_s
      13. Thread.pass
      14. }
      15. }
      16. puts( "Starting threads..." )
      17. t1.run
      18. t2.run
      19. t1.join
      20. puts( arr )