在 Ruby 中,提供三种实现同步的方式,分别是:
通过 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
以上代码执行结果为:
除了使用 lock 锁定变量,还可以使用 try_lock 锁定变量,还可以使用 Mutex.synchronize 同步对某一个变量的访问。
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
程序的输出:
线程可以有其私有变量,线程的私有变量在线程创建的时候写入线程。可以被线程范围内使用,但是不能被线程外部进行共享。
但是有时候,线程的局部变量需要别别的线程或者主线程访问怎么办?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}"
以上代码运行输出结果为:
主线程等待子线程执行完成,然后分别输出每个值。
线程的优先级是影响线程的调度的主要因素。其他因素包括占用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 表,使用 [] 和 []= 方法,我们实现了线程之间的数据共享。