Ruby线程同步控制

在 Ruby 中,提供三种实现同步的方式,分别是:

  1. 通过 Mutex 类实现线程同步
  2. 监管数据交接的 Queue 类实现线程同步
  3. 使用 ConditionVariable 实现同步控制

Mutex类实现线程同步

通过 Mutex 类实现线程同步控制,如果在多个线程钟同时需要一个程序变量,可以将这个变量部分使用 lock 锁定。 代码如下:

#!/usr/bin/ruby -w # -*- coding : utf-8 -*- puts "HaiCoder(www.haicoder.net)" require "thread" puts "Synchronize Thread" @num=200 @mutex=Mutex.new def buyTicket(num) @mutex.lock if @num>=num @num=@num-num puts "you have successfully bought #{num} tickets" else puts "sorry,no enough tickets" end @mutex.unlock end ticket1=Thread.new 10 do 10.times do |value| ticketNum=15 buyTicket(ticketNum) sleep 0.01 end end ticket2=Thread.new 10 do 10.times do |value| ticketNum=20 buyTicket(ticketNum) sleep 0.01 end end sleep 1 ticket1.join ticket2.join

以上代码执行结果为:

02_Ruby多线程.png

除了使用 lock 锁定变量,还可以使用 try_lock 锁定变量,还可以使用 Mutex.synchronize 同步对某一个变量的访问。

Queue类实现线程同步

Queue 类就是表示一个支持线程的队列,能够同步对队列末尾进行访问。不同的线程可以使用统一个对类,但是不用担心这个队列中的数据是否能够同步,另外使用 SizedQueue 类能够限制队列的长度。

SizedQueue 类能够非常便捷的帮助我们开发线程同步的应用程序,应为只要加入到这个队列中,就不用关心线程的同步问题。

经典的生产者消费者问题:

#!/usr/bin/ruby -w # -*- coding : utf-8 -*- puts "HaiCoder(www.haicoder.net)" require "thread" puts "SizedQuee Test" queue = Queue.new producer = Thread.new do 10.times do |i| sleep rand(i) # 让线程睡眠一段时间 queue << i puts "#{i} produced" end end consumer = Thread.new do 10.times do |i| value = queue.pop sleep rand(i/2) puts "consumed #{value}" end end consumer.join

程序的输出:

03_Ruby多线程.png

线程变量

线程可以有其私有变量,线程的私有变量在线程创建的时候写入线程。可以被线程范围内使用,但是不能被线程外部进行共享。

但是有时候,线程的局部变量需要别别的线程或者主线程访问怎么办?ruby 当中提供了允许通过名字来创建线程变量,类似的把线程看做 hash 式的散列表。通过 []= 写入并通过 [] 读出数据。我们来看一下下面的代码:

#!/usr/bin/ruby -w # -*- coding : utf-8 -*- puts "HaiCoder(www.haicoder.net)" count = 0 arr = [] 10.times do |i| arr[i] = Thread.new { sleep(rand(0)/10.0) Thread.current["mycount"] = count count += 1 } end arr.each {|t| t.join; print t["mycount"], ", " } puts "count = #{count}"

以上代码运行输出结果为:

04_Ruby多线程.png

主线程等待子线程执行完成,然后分别输出每个值。

线程优先级

线程的优先级是影响线程的调度的主要因素。其他因素包括占用CPU的执行时间长短,线程分组调度等等。

可以使用 Thread.priority 方法得到线程的优先级和使用 Thread.priority= 方法来调整线程的优先级。线程的优先级默认为 0。 优先级较高的执行的要快。

一个 Thread 可以访问自己作用域内的所有数据,但如果有需要在某个线程内访问其他线程的数据应该怎么做呢? Thread 类提供了线程数据互相访问的方法,你可以简单的把一个线程作为一个 Hash 表,可以在任何线程内使用 []= 写入数据,使用 [] 读出数据。

athr = Thread.new { Thread.current["name"] = "Thread A"; Thread.stop } bthr = Thread.new { Thread.current["name"] = "Thread B"; Thread.stop } cthr = Thread.new { Thread.current["name"] = "Thread C"; Thread.stop } Thread.list.each {|x| puts "#{x.inspect}: #{x["name"]}" }

可以看到,把线程作为一个 Hash 表,使用 [] 和 []= 方法,我们实现了线程之间的数据共享。