- 论坛徽章:
- 49
|
老一套的至理名言道,并发编程很难,在Ruby中尤其如此。就是这个基本假设,导致许多Ruby爱好者们对类似Erlang和Scala的语言发生了兴趣。为了让一般的程序员更加容易地实现和理解并发系统,这些语言内置了对
Actor模式
(Actor Model,或译为Actor模型)的支持。
但是,真的需要到Ruby语言之外寻找能够使你的工作变轻松的并发原语(Concurrency Primitive)吗?这个问题的答案可能取决于你所需要的并发和可用性的层次,但在近几年情况已经确定无疑地成型了。尤其是
Celluloid
框架,已经带给我们一种便捷、干净的方法,用来在Ruby中实现基于Actor模式的并发系统。
为了领会Celluloid能为你做什么,首先你需要理解Actor模式是什么,以及同并发编程中传统的直接使用线程和锁的方式相比,Actor模式具有什么好处。在本文中,我们试图通过使用三种不同的方法解决同一个经典的并发谜题来弄清楚这些知识点。这三种方法是:使用Ruby内置的原语(线程和互斥锁)、使用Celluoid框架、使用一个我们从头创建的Actor模式的最小实现。
如果你现在还不是并发方面的专家,看完本文后当然也不会让你成为这样的专家,但能够使你在一些基本概念方面具有一个有利的开端,这些基本概念可以帮助你决定如何解决你自己项目中的并发编程问题。让我们开始吧!
哲学家就餐问题
哲学家就餐
问题是由Edsger Djisktra在1965年提出的,用来说明在多个进程为获得互斥性资源的使用权进行竞争时可能会出现哪些问题。
在这个问题中,五个哲学家正进行聚餐。他们坐在一个圆形餐桌周围,每个哲学家面前都有一碗米饭。另外还有五根筷子,每个哲学家一边一根。哲学家要花时间来思考人生的意义。无论何时只要他们饿了,他们就要试图吃饭。但是哲学家需要在每个手中都握有一根筷子才能弄到米饭吃。如果任何其他哲学家已经拿走了他所需要的两根筷子中的一根,那么这个饥饿的哲学家将会等待,直到那根被拿走的筷子可用为止。
这个问题令人关注是因为,如果不恰当地解决该问题,就能很容易地导致死锁问题。我们马上就会看看这些问题,但首先让我们把这个问题域(problem domain)转化为几个基本的Ruby对象。
对餐桌及其上的筷子进行建模
我们即将在本文中进行讨论的所有这三种解决办法都会用到筷子类Chopstic和餐桌类Table。这两个类的定义如下所示:
01
class Chopstick
02
def initialize
03
@mutex = Mutex.new
04
end
05
06
def take
07
@mutex.lock
08
end
09
10
def drop
11
@mutex.unlock
12
13
rescue ThreadError
14
puts "Trying to drop a chopstick not acquired"
15
end
16
17
def in_use?
18
@mutex.locked?
19
end
20
end
21
22
class Table
23
def initialize(num_seats)
24
@chopsticks = num_seats.times.map { Chopstick.new }
25
end
26
27
def left_chopstick_at(position)
28
index = (position - 1) % @chopsticks.size
29
@chopsticks[index]
30
end
31
32
def right_chopstick_at(position)
33
index = (position + 1) % @chopsticks.size
34
@chopsticks[index]
35
end
36
37
def chopsticks_in_use
38
@chopsticks.select { |f| f.in_use? }.size
39
end
40
end
Chopstick类仅仅是对普通的Ruby互斥锁(Mutex)进行了一个简单的封装, 该互斥锁将确保两个哲学家不能在同一时刻拿到同一根筷子。Table类处理谜题中的几何问题,它了解餐桌周围的每个座位在哪里,哪根筷子在某个座位的左边还是右边,它还知道当前有多少根筷子在使用中。
既然你已经看完了对此问题进行模型化后得到的基本领域对象(domain object),下面我们看看实现哲学家行为的不同的方法。我们将以不能正确解决问题的方法开始。
造成死锁的解决方案
以下所示的哲学家类,Philosopher,似乎是该问题最直截了当的一个解决方案了,但它有一个致命的缺陷,因此它不是一个线程安全的(Thread Safe)方案。你能找出这个缺陷吗?
01
class Philosopher
02
def initialize(name)
03
@name = name
04
end
05
06
def dine(table, position)
07
@left_chopstick = table.left_chopstick_at(position)
08
@right_chopstick = table.right_chopstick_at(position)
09
10
loop do
11
think
12
eat
13
end
14
end
15
16
def think
17
puts "#{@name} is thinking"
18
end
共6页:
上一页
1
2
3
4
5
6
下一页
时间:2013-02-21 08:55来源:开源中国社区 作者:oschina责任编辑:zhangkai
本文来自ChinaUnix新闻频道,如果查看原文请点:http://news.chinaunix.net/opensource/2013/0221/2642628.shtml |
|