3.4.1 在并发系统中的时间的本性

3.4.1 在并发系统中的时间的本性

在表面上,时间似乎是自然的。它强加给事件一个顺序。对于
任何一个事件A和B,或者是A在B前, B在A前, A和B是同时的。
例如,还是银行账号的例子,假定从一个联合的刚开始有100元的账号中,
彼得取10元,保罗取25元,账号中还剩65元。依赖于这两次取款的顺序,在
账号中的余额的序列可能是 100—>90->65 或者是100->75->65 。
在一个银行系统的计算机实现中,这个账号余额的改变序列能被模型化。
这是用一系列的对变量balance的赋值语句模型化的。

在一个复杂的场景下,这个视图有问题。假定彼得和保罗还有其他人正在操作同一个账号,
通过分布在全世界的银行机器的网络上,进行操作。
在账号的余额的实际序列中,将极其关键性依赖读取时间的细节和机器间的通信的细节。

在并发系统的设计中,事件的顺序的不确定性,能够产生很严重的问题。例如,假定
彼得和保罗要取款,被实现为两个独立的执行过程,共享一个变量balance,在3.1.1部分
中的程序指定了任何一个执行过程:

(define (withdraw amount)
   (if  (>= balance amount) 
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds"
   )
)

如果两个执行过程单独的操作,然后彼得可能测试余额,试图取款一个合法的数额。
然而,在保罗可能取款发生在彼得检查余额与完成取款之间,因此彼得的测试是无效的。

事情可能变得更糟,考虑如下的表达式:

(set! balance (- balance amount))

它执行作为取款过程的一部分。这包括了三个步骤:1取变量的值,2计算新的值,3设置新的值。
如果彼得和保罗的取款,并发的执行这个语句,那么这两个取款在读取变量的值和设置新的值的
顺序可能是混乱的。

在图3.29中时序图显示了一个事件的顺序,当余额开始时是100,彼得取10,保罗取25,然后余额的
最终值是75。正如图中所示,这个异常的原因是保罗的对balance的75的赋值是在余额的被减数是100的假设之下的。
这个假设,然而,在彼得把余额修改为90时,是变得无效了。对于银行系统来说,这是一个重大的失败,
因为在系统中的金钱的总金额不能被修改。在交易前,金额的总数是100,之后是彼得10,保罗25,银行里是75。

这里显示的通常的现象是一些过程可能共享一个状态变量。使它复杂的是超过一个过程可能试图同时操作共享的状态变量。
对于银行系统的例子,在任何一个交易中,任何一个客户,应该能够操作,好像其它的客户不存在似的。当一个客户
以一种方式修改了余额,他必须能够假定余额在修改前,这个余额仍然是他认为对的那个值。

*并发程序的正确的行为

如上的例子,揭示了一个能溜进并发程序的微妙的错误。这个复杂性的根本是依赖于在不同的执行过程中,
对共享的变量的赋值操作。我们已经知道了,我们在写的程序中使用set!必须很小心才行。因为计算的结果依赖于
赋值发生的顺序。在并发的过程中,我们必须格外注意赋值的事,因为我们不可能有能力控制由不同的执行过程做出的
赋值的执行顺序。如果有一些这样的修改是并发的(如两个存款人读取一个联合的账户),我们需要一些方式来确保,
我们的系统的行为的正确性。例如,在从一个联合的账户中取款的例子,我们必须确保金额被修改了。为了并发程序的执行
的正确性,我们可能不得不在并发的执行上,加入一些限制。

在并发上,一个可能的限制是规定没有两个操作能同时修改任何共享的状态变量。这是一个极其好的
要求。对于分布式的银行,这要求系统设计者确保在一个时刻只有一个交易发生。这是很低效的,也是不可能的。
图3.30显示了彼得和保罗共享一个银行的账户,如同保罗有一个私人的账户一样。图中显示了从共享账户的两次取款
和一次存款到保罗的私人账户。这两个从共享账户的取款必须不能并发。保罗的存款与取款也不能并发。但是
允许保罗存款到他的私人账户与彼得从联合账户中取款的并发执行是没有什么问题的。


在并发上,一个更不严格的限制是好像执行过程以一定的顺序在运行,
确保一个并发的系统产生相同的结果.对于这个要求,这有两个重要的方面.
第一,它并不要求执行的过程以实际上的顺序执行,但是仅要求产生的结果与顺序执行的
结果是一样的.对于图3.30中的例子,银行账户系统的设计者能够安全地允许保罗的存款
与彼得的取款并发的发生,因为不影响结果。第二,并发的系统可能产生有超过一个
的正确的结果。因为我们仅要求结果与一个特定的顺序执行后的结果一致。例如,彼得
和保罗的联合账户开始于100,彼得存入40,而保罗并发地取账户的余额的一半。然后
顺序地执行,可能账户余额上是70或者是90(见练习3.38)

为了并发程序的正确执行,这还有一个更弱的需求。为了模拟混乱的一个程序可能
由更多的执行过程组成,任何一个都表示一个小的空间,并发地更新它们的值。任何一个
执行过程都重复地修改它的值为它的值和它的邻居的值的平均值。这个平均值的算法的
正确答案依赖操作执行的顺序,对于共享值的并发使用的任何限制都是不需要的。

练习3.38
假定彼得,保罗,和马莉共享一个联合的银行账户,初始化为100,并发地,彼得存10,
保罗取20,马莉取账户余额的一半,以如下的表达式执行:

彼得:(set! balance (+ balance 10))
保罗:(set! balance (- balance 20))
马莉:(set! balance (- balance (/ balance 2)))

a. 这三个交易结束后,列出余额所有的可能的不同的值。
假定的是银行系统强制这三个交易以某种顺序运行。

b. 如果系统允许执行过程一齐执行,还有什么其它的可能的值?
画出时序图,解释这些值是如何产生的?

猜你喜欢

转载自blog.csdn.net/gggwfn1982/article/details/81673224