线程优先级

到目前为止,我们已经让 Ruby 完全自由地以任何方式切换线程之间的时间。但有时候一个线程比其它线程更重要。例如,如果你正在编写一个文件复制程序,其中一个线程用于执行实际复制,而另一个线程用于显示进度条,那么给文件复制线程更多时间是有意义的。

有时候当前正在执行的线程特别想要将执行时间给予其它线程。原则上,这是通过调用 Thread.pass 方法完成的。然而,在实践中,这可能无法产生你期望的结果。pass 方法将在本章末尾的“深入探索”部分中详细讨论。

Ruby 允许你分配整数值以指示每个线程的优先级(priority)。理论上,具有较高优先级的线程比具有较低优先级的线程分配更多的执行时间。在实践中,事情并不那么简单,因为其它因素(例如运行线程的顺序)可能会影响给予每个线程的时间量。而且,在非常短的程序中,可能无法确定改变优先级的效果。我们到目前为止使用的单词和数字线程示例太短,无法显示任何差异。因此,让我们来看一个稍微工作密集的程序 - 一个运行三个线程的程序,每个线程调用一个方法五十次,以便计算 50 的阶乘。

threads4.rb
  1. def fac(n)
  2. n == 1 ? 1 : n * fac(n-1)
  3. end
  4. t1 = Thread.new{
  5. 0.upto(50) {fac(50); print( "t1\n" )}
  6. }
  7. t2 = Thread.new{
  8. 0.upto(50) {fac(50); print( "t2\n" )}
  9. }
  10. t3 = Thread.new{
  11. 0.upto(50) {fac(50); print( "t3\n" )}
  12. }

我们现在可以为每个线程设置特定的优先级:

  1. t1.priority = 0
  2. t2.priority = 0
  3. t3.priority = 0

在这种情况下,每个线程的优先级是相同的,因此没有线程将被赋予操作的最大时间切片,并且所有三个线程的结果将像通常一样混乱的出现。现在尝试更改 t3 的优先级:

  1. t3.priority = 1

这次运行代码时,t3 将占用大部分时间并且(很大可能)在其它线程之前执行。其它线程可能会在一开始就得到关注,因为它们是以相同的优先级创建的,并且优先级仅在它们开始运行后才会更改。当 t3 结束时,t1t2 应该或多或少平等地分享时间。

因此,假设你希望 t1t2 首先运行,或多或少地共享时间,并且仅在这两个线程完成后运行 t3。这是我的第一次尝试;你可能想自己尝试一下:

  1. t1.priority = 2
  2. t2.priority = 2
  3. t3.priority = 1

嗯,最终结果不是我想要的!似乎线程是按顺序运行的,根本没有时间切片!好的,只是为了它,让我们尝试一些负数:

  1. t1.priority = -1
  2. t2.priority = -1
  3. t3.priority = -2

欢呼!这还差不多。这次,t1t2 同时运行(在设置线程优先级之前,你可能还会看到 t3 短暂运行);然后 t3 运行。那么为什么负值会起作用但正值却不会呢?

负值本身没有什么特别之处。但是,你需要记住,每个进程至少有一个运行的线程 - 主线程 - 这也有优先级。它的优先级恰好为 0。