编程时的注意事项

讨论并发编程中的错误结果、低效、死锁和不公平问题,以及正确行为的定义。

编程时的注意事项

即使有了串行化器,写好成功处理并发的程序也不容易。事实上,当今广泛使用的所有操作系统在这方面都有 bug;例如,Unix 系统预计每一两个月就会因并发 bug 崩溃一次。

为了使讨论具体化,让我们考虑一个航空订票系统,它为全球数千名同时使用的用户服务。以下是可能出问题的环节:

  • 错误的结果。 最严重的问题是同一个座位被预订给两个不同的人。正如给 x 加 1 的情况一样,订票系统必须首先找到一个空座位,然后把该座位标记为已占用。这个先读取再修改数据库的序列必须受到保护。

  • 低效。 确保结果正确的一个非常简单的办法是:用单个串行化器保护整个订票数据库,使得一次只有一个人能发出请求。但这是不可接受的解决方案;数千人等着订票,而且大多数不是为了同一航班。

  • 死锁。 假设有人想去一个没有直飞航班城市。我们必须确保在承诺任一预订之前,能在同一天订到 A 航班的座位和中转 B 航班的座位。这可能意味着我们需要同时使用两个串行化器,每个航班一个。假设我们这样说

    (serializer-A (serializer-B (lambda () ...))))

    同时另一个人说

    (serializer-B (serializer-A (lambda () ...))))

    时间上的安排可能刚好让我们拿到串行化器 A,另一个人拿到串行化器 B,然后我们各自卡住等待对方(永远!)。

  • 不公平。 这不是每种情况下都是问题,但有时你会想避免那种总是让某个过程优先于另一个过程的死锁解决方案。如果高优先级的过程很贪婪,低优先级的过程可能永远轮不到使用共享数据。

并发程序的正确行为

根据你编写的具体程序,正确行为的定义可能不同。典型地,如果并发程序产生的结果与这些过程以某种顺序顺序运行时的结果相同,就说它表现出正确行为。这个要求有两个重要方面。

第一,它并不要求过程真的顺序运行,只要求产生的结果与它们顺序运行时的结果相同。

第二,并发程序可能产生多个可能的”正确”结果之一,因为我们只要求结果与某种顺序运行的结果相同。