熟悉线程的朋友应该对经典的售票员卖票程序不陌生。全国各地有很多售票点同时发售车票,车票在这里作为一个公用的资源,各地的售票中心访问资源,打印车票给顾客。我们来看一下ruby实现这个简单功能的代码吧:
# 线程-买票实例
class Ticket
attr_reader :ticket
attr_reader :value
def initialize
@ticket = %w(a b c d e f g h i j k l m n o p q r s t u v w x y z)
@value = 0
end
def buy_ticket
@k = @ticket
if @k.empty?
puts "no result"
@value +=1
else
sleep(3*rand)
puts "the ticket is #{@k.last} "
sleep(3*rand)
@value +=1
puts "OK #{@value}"
@ticket.pop
puts "**************分隔符******************"
end
end
end
a = Ticket.new
t1 = Thread.new { 10.times {puts "线程1" ; a.buy_ticket } }
t2 = Thread.new { 10.times { puts "线程2"; a.buy_ticket } }
t3 = Thread.new { 10.times { puts "线程3"; a.buy_ticket } }
t1.join
t2.join
t3.join
我们开了3个线程分别表示不同的3个售票点同事发售车票,我们在发售车票的时候使用了sleep来模拟工作人员操作的时间。
@ticket = %w(a b c d e f g h i j k l m n o p q r s t u v w x y z)
@ticket这个数组用来表示一组车票。好我们来运行这个程序,看能看到什么结果
线程1线程2线程3 the ticket is z OK 1 **************分隔符****************** 线程3 the ticket is y the ticket is y the ticket is y OK 2 **************分隔符****************** 线程2 OK 3 **************分隔符****************** 线程1 OK 4 **************分隔符****************** 线程3 the ticket is v the ticket is v the ticket is v OK 5
我们可以看见,有的车票被多个窗口同时卖出,当然这在规定当中是不允许的。这个是为什么呢?我们来看一下程序。乍一看是肯定没有什么问题的。细细想想,假设我们的线程1进入卖票程序,并查看到当前可以买y票。线程1睡眠一段时间,假设这段时间正好线程2进来了,并执行了卖票程序,此时线程1并未实际意义上的把票卖了出去,这就导致了两个线程卖出了同一张票。
那么有没有解决办法呢?比如说,在线程1进入程序的时候,就把票给锁死了,这样线程2来的时候就不能对该张票进行操作。在ruby当中有个监视器monitor可是实现该功能。我们对买票这个操作进行监视控制,只能同时一个人来操作。好吧,我们来看一下下面的代码:
# 线程-买票实例
require 'monitor'
class Ticket
attr_reader :ticket
attr_reader :value
def initialize
@ticket = %w(a b c d e f g h i j k l m n o p q r s t u v w x y z)
@value = 0
end
def buy_ticket
@k = @ticket
if @k.empty?
puts "no result"
@value +=1
else
sleep(3*rand)
puts "the ticket is #{@k.last} "
sleep(3*rand)
@value +=1
puts "OK #{@value}"
@ticket.pop
puts "**************分隔符******************"
end
end
end
a = Ticket.new
k = Monitor.new
t1 = Thread.new { 10.times {k.synchronize{puts "线程1" ; a.buy_ticket }} }
t2 = Thread.new { 10.times {k.synchronize{puts "线程2" ; a.buy_ticket }} }
t3 = Thread.new { 10.times {k.synchronize{puts "线程3" ; a.buy_ticket }} }
t1.join
t2.join
t3.join
puts a.value
我们使用了监视器来对资源进行监视,不能同时由多个线程进行操作,避免重复卖票的情况,这中间的核心部分就是如下所示:
k = Monitor.new
t1 = Thread.new { 10.times {k.synchronize{puts "线程1" ; a.buy_ticket }} }
t2 = Thread.new { 10.times {k.synchronize{puts "线程2" ; a.buy_ticket }} }
t3 = Thread.new { 10.times {k.synchronize{puts "线程3" ; a.buy_ticket }} }
当然,我们可以不对该操作进行监视,我们对票据资源进行监视。也会使用到synchronize。这里就不再赘述,想知道的可以参考programming ruby。
好拉。今天就写这么多吧。o(∩_∩)o...哈哈