Ruby线程同步实例

##一个错误的商品购买实例

@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, shiftpop的别名方法
length队列的长度
sizelength的别名方法
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元,买了这件商品
223916_bL9y_2663968.jpg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值