这并不能直接回答问题,但我认为在此处发布它可能会有所帮助,因为它比维基百科更好,并且链接可能有一天会失效。
链接 - http://www.celticwolf.com/blog/2010/04/27/what-is-a-race-condition/
维基百科对竞态条件有很好的描述,但如果您不了解编程的基础知识,就很难理解。我将尝试用不太专业的术语来解释它,使用上面描述的生成标识符的示例。我还将使用人类活动的类比来尝试传达这些想法。
竞态条件是两个或多个程序(或单个程序的独立部分)都尝试同时获取某些资源,从而导致错误答案或冲突。该资源可以是信息,例如下一个可用的约会时间,也可以是对某些内容的独占访问权,例如电子表格。如果您曾经使用 Microsoft Excel 编辑共享驱动器上的文档,您可能有过被 Excel 告知其他人已经在编辑电子表格的经历。此错误消息是 Excel 优雅地处理潜在竞争条件和防止错误的方式。
程序的一个常见任务是确定某种下一个可用值,然后分配它。这种技术用于发票号码、学生证等。这是一个以前解决过的老问题。最常见的解决方案之一是允许存储数据的数据库生成数字。还有其他解决方案,它们各有优缺点。
不幸的是,对这一领域一无所知或根本不擅长编程的程序员经常尝试自己动手。聪明的人很快发现这是一个比看起来复杂得多的问题,并寻找现有的解决方案。坏人永远不会看到问题,或者一旦看到问题,就会坚持在不修复错误的情况下使他们不可行的解决方案变得更加复杂。让我们以学生证为例。新手程序员说“要知道下一个学号应该是什么,我们只需获取最后一个学号并增加它。”以下是幕后发生的事情:
- 贝蒂,管理员。招生办公室的助理启动了学生管理程序。请注意,这实际上只是在她的 PC 上运行的程序的副本。它通过学校网络与数据库服务器通信,但无法与其他 PC 上运行的程序的其他副本通信。
- Betty 为 Bob Smith 创建一个新的学生记录,输入所有信息。
- 在 Betty 输入数据时,另一位管理员 George。助理,在他的 PC 上启动学生管理程序并开始为 Gina Verde 创建记录。
- George 的打字速度比较快,所以他和 Betty 在同一时间完成。他们都同时点击了“保存”按钮。
- Betty 的程序连接到数据库服务器并获取正在使用的最高学号 5012。
- George 的程序同时对同一个问题得到相同的答案。
- 两个程序都决定他们正在保存的记录的新学生 ID 应该是 5013。他们将该信息添加到记录中,然后将其保存在数据库中。
- 现在 Bob Smith(Betty 的学生)和 Gina Verde(George 的学生)拥有相同的学生证。
此学生证将附加到各种其他记录中,从成绩到食堂的用餐卡。最终这个问题会暴露出来,有人将不得不花费大量时间为其中一个人分配一个新的 ID 并整理出混乱的记录。
当我向人们描述这个问题时,通常的反应是“但这在实践中多久会发生一次?从来没有,对吧?”。错误的。首先,当您的员工进行数据输入时,通常每个人都在相对较短的时间内完成。这增加了重叠的机会。如果该应用程序是对公众开放的 Web 应用程序,那么两个人同时点击“保存”按钮的几率会更高。我最近在生产系统中看到了这一点。这是一个公开测试版的网络应用程序。使用率很低,每天只有几个人注册。尽管如此,六对人在几个月的时间内设法获得了相同的 ID。如果您想知道,不,我和我团队中的任何人都没有编写该代码。然而,我们对这个问题发生了多少次感到非常惊讶。事后看来,我们不应该这样做。这真的是墨菲定律的简单应用。
如何避免这个问题?最简单的方法是使用经过充分测试的问题的现有解决方案。所有主要数据库(MS SQL Server、Oracle、MySQL、PostgreSQL 等)都有一种方法可以增加数字而不会产生重复。 MS SQL server 称其为“identity”列,而 MySQL 称其为“auto number”列,但功能相同。每当您插入新记录时,都会自动创建一个新标识符并保证其唯一性。这将使上述情况发生如下变化:
- 贝蒂,管理员。招生办公室的助理启动了学生管理程序。请注意,这实际上只是在她的 PC 上运行的程序的副本。它通过学校网络与数据库服务器通信,但无法与其他 PC 上运行的程序的其他副本通信。
- Betty 为 Bob Smith 创建一个新的学生记录,输入所有信息。
- 在 Betty 输入数据时,另一位管理员 George。助理,在他的 PC 上启动学生管理程序并开始为 Gina Verde 创建记录。
- George 的打字速度比较快,所以他和 Betty 在同一时间完成。他们都同时点击了“保存”按钮。
- Betty 的程序连接到数据库服务器并将要保存的记录交给它。
- George 的程序同时交出另一条记录进行保存。
- 数据库服务器将两条记录放入队列中,一次保存一条,为它们分配下一个可用编号。
- 现在 Bob Smith(Betty 的学生)的 ID 为 5013,Gina Verde(George 的学生)的 ID 为 5014。
有了这个解决方案,复制就没有问题了。多年来,制造商和用户都反复测试了为每个数据库服务器执行此操作的代码。全世界数以百万计的应用程序都依赖它并每天继续对其进行压力测试。任何人都可以对他们的本土解决方案说同样的话吗?
至少有一种经过充分测试的方法可以在软件中而不是在数据库中创建标识符:uuids(通用唯一标识符)。但是,uuid 采用xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 的形式,其中“x”代表十六进制数字(0-9 和 a-f)。您想将其用于发票号、学生证或公众看到的其他标识符吗?应该不会吧。
总而言之,当两个程序或程序的两个独立部分尝试同时访问某些信息或访问资源时,就会发生竞争条件,从而导致错误,无论是计算错误还是标识符重复或对资源的访问冲突。竞争条件的类型比我在这里介绍的要多得多,它们会影响软件和硬件的许多其他领域。