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