##一个错误的商品购买实例
@count = 1000 #商品总数量
def buy(num) #购买商品的方法
if @count >= num #判断商品数量是否比商品总数小
Thread.pass #暂停线程
@count -= num
puts "#{num}件购买成功\n"
else
puts "#{num}件购买失败\n"
end
end
#三个线程并发购买
t1 = Thread.new(999) {|num| buy(num)}
t2 = Thread.new(10) {|num| buy(num)}
t3 = Thread.new(2) {|num| buy(num)}
t1.join
t2.join
t3.join
运行结果
#第一次
999件购买成功
10件购买成功
2件购买成功
#第二次
2件购买成功
10件购买成功
999件购买失败
通过运行上面代码发现,商品购买成功的数量已经超出了商品总数量,因此这段代码是有问题的。
##解决多线程购买商品数量问题
###方法1:使用Mutex库的lock和unlock方法
修复后的代码:
@count = 1000 #商品总数量
@mutex = Mutex.new
def buy(num) #购买商品的方法
@mutex.lock #锁定当前线程资源,还有try_lock方法,如果另一个线程已锁定资源,try_lock返回false而不是等待
if @count >= num #判断商品数量是否比商品总数小
Thread.pass #暂停线程
@count -= num
puts "#{num}件购买成功\n"
else
puts "#{num}件购买失败\n"
end
@mutex.unlock #解锁
end
#三个线程并发购买
t1 = Thread.new(999) {|num| buy(num)}
t2 = Thread.new(10) {|num| buy(num)}
t3 = Thread.new(2) {|num| buy(num)}
t1.join
t2.join
t3.join
运行结果
999件购买成功
10件购买失败
2件购买失败
###方法2:使用Mutex的synchronize方法
修复后的代码:
@count = 1000 #商品总数量
@mutex = Mutex.new
def buy(num) #购买商品的方法
@mutex.synchronize do #使用synchronize锁定当前线程,接受一个代码块
if @count >= num #判断商品数量是否比商品总数小
Thread.pass #暂停线程
@count -= num
puts "#{num}件购买成功\n"
else
puts "#{num}件购买失败\n"
end
end
end
#三个线程并发购买
t1 = Thread.new(999) {|num| buy(num)}
t2 = Thread.new(10) {|num| buy(num)}
t3 = Thread.new(2) {|num| buy(num)}
t1.join
t2.join
t3.join
运行结果
#第一次
2件购买成功
10件购买成功
999件购买失败
#第二次
999件购买成功
10件购买失败
2件购买失败
##Ruby中的同步队列Queue
Queue类是一个支持多线程的同步队列,能够同步对队列末尾进行访问,即不同的线程可以使用同一个队列,而不用担心同步问题。
SizedQueue和Queue类类似,SizedQueue类能够限制队列的长度,使用max和max=方法可以获取和设置队列的最大长度。
###一个简单的例子
buff = SizedQueue.new(20)
puts "队列的最大长度:#{buff.max}"
buff.max = 50
puts "修改后的队列最大长度:#{buff.max}"
运行结果
队列的最大长度:20
修改后的队列最大长度:50
Queue常用的方法:
方法 | 含义 |
---|---|
push | 将元素插入队列 |
enq, << | push的别名方法 |
pop | 将元素从队列取出 |
deq, shift | pop的别名方法 |
length | 队列的长度 |
size | length的别名方法 |
empty? | 队列是否为空 |
clear | 清空队列 |
num_waiting | 获取等待访问队列的线程数 |
###队列生产者和消费者实例
例子中,使用线程的休眠时间让生产者生产的数据多于消费者消费的数据。
buffer = SizedQueue.new(2)
producer = Thread.new do #生产者线程
item = 0
loop do
sleep rand(0)
puts "生产者生产:#{item}"
buffer.push item
item += 1
end
end
consumer = Thread.new do #消费者线程
loop do
sleep rand(0) + 0.9
item = buffer.pop
puts "消费者消费:#{item}"
end
end
sleep 60
运行结果
生产者生产:0
消费者消费:0
生产者生产:1
生产者生产:2
生产者生产:3
消费者消费:1
生产者生产:4
消费者消费:2
生产者生产:5
消费者消费:3
生产者生产:6
###ConditionVariable
有这样一个场景,系统中有同时运行的两个线程,一个商家线程,一个消费者线程,商家给出价格(同一件商品的价格可能不相同),消费者根据价格购买商品。要实现这个实例,需要使用ConditionVariable类,它表示一个简单的信号量,它关联一个特定的资源,在同步代码块范围中使用。当一个线程运行到某个时间点,必须需要某个资源,而这个资源又由另一个线程负责提供。
代吗:
cv = ConditionVariable.new
mutex = Mutex.new
price = 0
flag = false
consumer = Thread.new(10) do |val|
val.times do |i|
mutex.lock
if price == 0
puts "消费者:这件商品多少钱?"
cv.wait(mutex)
end
if flag
puts "消费者给了商家:#{price}元,买了这件商品"
puts "------------------------"
price = 0
flag = false
cv.signal
end
mutex.unlock
end
end
shangjia = Thread.new(10) do |val|
val.times do |i|
mutex.lock
if !flag
price = rand(300)
puts "商家:#{price}"
flag = true
cv.signal
else
cv.wait(mutex)
end
mutex.unlock
end
end
consumer.join
shangjia.join
运行结果
消费者:这件商品多少钱?
商家:144
消费者给了商家:144元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:172
消费者给了商家:172元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:58
消费者给了商家:58元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:161
消费者给了商家:161元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:149
消费者给了商家:149元,买了这件商品
------------------------
消费者:这件商品多少钱?
test.rb:214:in `join': deadlock detected (fatal) #出现了死锁
上面代码产生了死锁,解决办法,给生产者和消费者线程单次处理加上一个不相同的休眠时间。
新的代码
cv = ConditionVariable.new
mutex = Mutex.new
price = 0
flag = false
consumer = Thread.new(10) do |val|
val.times do |i|
mutex.synchronize do
if price == 0
puts "消费者:这件商品多少钱?"
cv.wait(mutex)
end
if flag
puts "消费者给了商家:#{price}元,买了这件商品"
puts "------------------------"
price = 0
flag = false
cv.signal
end
end
sleep 0.1
end
end
shangjia = Thread.new(10) do |val|
val.times do |i|
mutex.synchronize do
if !flag
price = rand(300)
puts "商家:#{price}"
flag = true
cv.signal
else
cv.wait(mutex)
end
end
sleep 0.2
end
end
consumer.join
shangjia.join
运行结果
消费者:这件商品多少钱?
商家:225
消费者给了商家:225元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:272
消费者给了商家:272元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:19
消费者给了商家:19元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:3
消费者给了商家:3元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:31
消费者给了商家:31元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:290
消费者给了商家:290元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:289
消费者给了商家:289元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:206
消费者给了商家:206元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:285
消费者给了商家:285元,买了这件商品
------------------------
消费者:这件商品多少钱?
商家:275
消费者给了商家:275元,买了这件商品