Ruby - 多线程

传统程序有一个执行线程构成程序的语句或指令按顺序执行,直到程序终止.

多线程程序有更多而不是一个执行的线程.在每个线程内,语句是顺序执行的,但是线程本身可以在多核CPU上并行执行.通常在单个CPU机器上,多个线程实际上并不是并行执行,而是通过交错执行线程来模拟并行性.

Ruby可以很容易地编写多线程程序 Thread 类. Ruby线程是一种在代码中实现并发的轻量级高效方法.

创建Ruby线程

要启动一个新线程,只需关联一个块调用 Thread.new .将创建一个新线程来执行块中的代码,原始线程将立即从 Thread.new 返回并继续执行下一个语句 :

# Thread #1 is running here
Thread.new {
   # Thread #2 runs this code
}
# Thread #1 runs this code

示例

这是一个示例,它显示了我们如何使用多线程Ruby程序.

#!/usr/bin/ruby

def func1
   i = 0
   while i<=2
      puts "func1 at: #{Time.now}"
      sleep(2)
      i = i&plus;1
   end
end

def func2
   j = 0
   while j<=2
      puts "func2 at: #{Time.now}"
      sleep(1)
      j = j&plus;1
   end
end

puts "Started At #{Time.now}"
t1 = Thread.new{func1()}
t2 = Thread.new{func2()}
t1.join
t2.join
puts "End at #{Time.now}"

这将产生以下结果 :

Started At Wed May 14 08:21:54 -0700 2008
func1 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:55 -0700 2008
func1 at: Wed May 14 08:21:56 -0700 2008
func2 at: Wed May 14 08:21:56 -0700 2008
func1 at: Wed May 14 08:21:58 -0700 2008
End at Wed May 14 08:22:00 -0700 2008

线程生命周期

使用 Thread.new 创建新线程.您还可以使用同义词 Thread.start Thread.fork .

创建线程后无需启动线程当CPU资源可用时,它会自动开始运行.

Thread类定义了许多在线程运行时查询和操作线程的方法.一个线程运行与 Thread.new 调用相关联的块中的代码,然后它停止运行.

该块中最后一个表达式的值是线程的值,可以通过调用Thread对象的 value 方法获得.如果线程已运行完成,则该值立即返回线程的值.否则, value 方法会阻塞,直到线程完成后才会返回.

类方法 Thread.current 返回Thread表示当前线程的对象.这允许线程自己操纵.类方法 Thread.main 返回表示主线程的Thread对象.这是Ruby程序启动时开始的初始执行线程.

您可以通过调用该线程的 Thread.join 等待特定线程完成方法.调用线程将阻塞,直到给定的线程完成.

线程和异常

如果在主线程中引发异常,而不是在任何地方处理,Ruby解释器打印一条消息并退出.在主线程以外的线程中,未处理的异常会导致线程停止运行.

如果线程 t 由于未处理的异常而退出,而另一个线程 s 调用 t.join或t.value,然后在 s 中引发的异常在 s 中引发>.

如果 Thread.abort_on_exception false ,则默认情况下,未处理的异常只会杀死当前线程以及所有其他线程继续运行.

如果您希望任何线程中的任何未处理的异常导致解释器退出,请将类方法 Thread.abort_on_exception 设置为 true .

t = Thread.new { ... }
t.abort_on_exception = true

线程变量

线程通常可以在创建线程时访问范围内的任何变量.线程块本地的变量是线程的本地变量,并且不是共享的.

线程类具有一个特殊功能,允许通过名称创建和访问线程局部变量.您只需将线程对象视为Hash,使用[] =写入元素并使用[]读取它们.

在此示例中,每个线程记录当前的值使用键 mycount 的threadlocal变量中的变量count.

#!/usr/bin/ruby

count = 0
arr = []

10.times do |i|
   arr[i] = Thread.new {
      sleep(rand(0)/10.0)
      Thread.current["mycount"] = count
      count &plus;= 1
   }
end

arr.each {|t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

这会产生以下结果 :

8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10

主线程等待子线程完成,然后打印出每个线程捕获的 count 的值.

线程优先级

影响线程调度的第一个因素是线程优先级:在低优先级线程之前调度高优先级线程.更准确地说,如果没有更高优先级的线程等待运行,线程将只获得CPU时间.

您可以使用优先级设置和查询Ruby Thread对象的优先级= 优先级.新创建的线程的启动优先级与创建它的线程相同.主线程以优先级0开始.

在线程开始运行之前无法设置线程的优先级.但是,线程可以提升或降低自己的优先级作为第一个操作.

线程排除

如果两个线程共享访问权限相同的数据,并且至少有一个线程修改了该数据,您必须特别注意确保没有线程能够看到处于不一致状态的数据.这称为线程排除.

Mutex 是一个实现简单信号量锁的类,用于对某些共享资源进行互斥访问.也就是说,在给定时间只有一个线程可以保持锁定.其他线程可以选择排队等待锁定变为可用,或者可以简单地选择立即获得错误,指示锁定不可用.

通过将所有访问权限放在共享上在互斥锁的控制下,我们确保一致性和原子操作.让我们尝试一些例子,第一个没有mutax,第二个有mutax :

没有Mutax的例子

#!/usr/bin/ruby
require 'thread'

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      count1 &plus;= 1
      count2 &plus;= 1
   end
end
spy = Thread.new do
   loop do
      difference &plus;= (count1 - count2).abs
   end
end
sleep 1
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这将产生以下结果 :

count1 :  1583766
count2 :  1583766
difference : 0

演示

#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      mutex.synchronize do
         count1 &plus;= 1
         count2 &plus;= 1
      end
   end
end
spy = Thread.new do
   loop do
      mutex.synchronize do
         difference &plus;= (count1 - count2).abs
      end
   end
end
sleep 1
mutex.lock
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这将产生以下结果 :

count1 :  696591
count2 :  696591
difference : 0

处理死锁

当我们开始使用 Mutex 对象进行线程排除时,我们必须是c是为了避免死锁.死锁是当所有线程都在等待获取另一个线程持有的资源时发生的情况.因为所有线程都被阻塞,所以它们无法释放它们所持有的锁.并且因为它们无法释放锁,所以没有其他线程可以获取这些锁.

这是条件变量进入图片的地方. 条件变量只是一个与资源相关联的信号量,用于保护特定的互斥锁.当您需要一个不可用的资源时,您需要等待一个条件变量.该操作会释放相应互斥锁上的锁定.当一些其他线程发出信号表明该资源可用时,原始线程将从等待中恢复,并同时重新获得关键区域的锁定.

示例

#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

cv = ConditionVariable.new
a = Thread.new {
   mutex.synchronize {
      puts "A: I have critical section, but will wait for cv"
      cv.wait(mutex)
      puts "A: I have critical section again! I rule!"
   }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
   mutex.synchronize {
      puts "B: Now I am critical, but am done with cv"
      cv.signal
      puts "B: I am still critical, finishing up"
   }
}
a.join
b.join

这将产生以下结果 :

A: I have critical section, but will wait for cv
(Later, back at the ranch...)
B: Now I am critical, but am done with cv
B: I am still critical, finishing up
A: I have critical section again! I rule!

线程状态

有五种可能的返回值对应于五种可能的状态,如下表所示. status 方法返回线程的状态.

Thread stateReturn value
Runnablerun
SleepingSleeping
Abortingaborting
Terminated normallyfalse
Terminated with exceptionnil

线程类方法

以下方法是由 Thread 类提供,它们适用于程序中可用的所有线程.这些方法将被称为使用 Thread 类名,如下所示 :

Thread.abort_on_exception = true

Sr.No.方法&说明
1

Thread.abort_on_exception

返回异常条件下全局中止的状态.默认值为 false .当设置为 true 时,如果在任何线程中引发异常,将导致所有线程中止(进程将退出(0))

2

Thread.abort_on_exception =

当设置为 true 时,如果引发异常,所有线程都将中止.返回新状态.

3

Thread.critical

返回全局线程关键条件的状态.

4

Thread.critical =

设置全局线程严重条件的状态并将其返回.设置为 true 时,禁止调度任何现有线程.不会阻止创建和运行新线程.某些线程操作(例如停止或终止线程,在当前线程中休眠以及引发异常)可能导致线程被安排,即使在关键部分也是如此.

5

Thread.current

返回当前正在执行的线程.

6

Thread.exit

终止当前运行的线程并安排另一个要运行的线程.如果此线程已被标记为被杀死,退出将返回线程.如果这是主线程或最后一个线程,则退出该进程.

7

Thread.fork { block }

Thread.new的同义词.

8

Thread.kill(aThread)

原因给定的一个线程退出

9

Thread.list

返回所有线程的 Thread 对象数组无论是可运行还是已停止.线程.

10

Thread.main

返回进程的主线程.

11

Thread.new([arg] *){| args |块}

创建一个新线程来执行块中给出的指令,并开始运行它.传递给 Thread.new 的任何参数都会传递到块中.

12

Thread.pass

调用线程调度程序将执行传递给另一个线程.

13

Thread.start( [ args ]* ) {| args | block }

基本上与 Thread.new 相同.但是,如果class Thread 是子类,那么在该子类中调用 start 将不会调用子类的 initialize 方法.

14

Thread.stop

停止执行当前线程,将其置于 sleep 状态,并安排执行另一个线程.将严重条件重置为false.

线程实例方法

这些方法适用于线程的实例.这些方法将被调用为使用 Thread 的实例,如下所示 :

#!/usr/bin/ruby

thr = Thread.new do   # Calling a class method new
   puts "In second thread"
   raise "Raise exception"
end
thr.join   # Calling an instance method join

Sr.No.方法&说明
1

thr [aSymbol]

属性参考 - 使用符号或 aSymbol 名称返回线程局部变量的值.如果指定的变量不存在,则返回 nil .

2

thr [aSymbol] =

属性赋值 - 设置或创建值一个线程局部变量,使用符号或字符串.

3

thr.abort_on_exception

返回异常中止的状态 thr 的条件.默认值为 false .

4

thr.abort_on_exception =

当设置为 true 时,会导致所有线程(如果在 thr 中引发异常,则中止(包括主程序).该过程将有效地退出(0).

5

thr.alive?

如果<i,则返回 true > thr 正在运行或正在睡觉.

6

thr.exit

终止 thr 并安排另一个要运行的线程.如果此线程已被标记为被杀死,退出将返回线程.如果这是主线程或最后一个线程,则退出进程.

7

thr.join

调用线程将暂停执行并运行 thr 的.在 thr 退出之前不会返回.当主程序退出时,任何未加入的线程都将被终止.

8

thr.key?

如果是给定的字符串,则返回 true 符号)作为线程局部变量存在.

9

thr.kill

Thread.exit 的同义词.

10

thr.priority

返回 thr 的优先级.默认值为零;优先级较高的线程将在优先级较低的线程之前运行.

11

thr.priority =

thr 的优先级设置为整数.优先级较高的线程将在优先级较低的线程之前运行.

12

thr.raise(anException)

thr 引发异常.调用者不必是 thr .

13

thr.run

唤醒 thr ,制作它有资格安排.如果不在关键部分,则调用调度程序.

14

thr.safe_level

返回 thr 的安全级别.

15

thr.status

如果 thr ,则返回 thr 的状态: sleep 正在睡眠或等待I/O,运行如果 thr 正在执行,如果 thr 正常终止则为假,并且 nil if thr 以异常终止.

16

thr.stop?

如果,则返回 true thr 已死或正在睡觉.

17

thr.value

通过 Thread.join 等待thr完成并返回价值.

18

thr.wakeup

thr 标记为有资格获得但是,它可能仍会在I/O上被阻止.