我应该照顾几乎肯定不会发生的比赛条件吗?


52

让我们考虑一下诸如GUI应用程序之类的应用程序,其中主线程几乎立即更新UI,而另一些线​​程正在通过网络轮询数据,或者可以保证需要5到10秒才能完成工作。

我收到了许多不同的答案,但是有些人说,如果这是统计上不可能的比赛条件,则完全不必担心,但其他人则说,即使有10 %-53%(我您不知道数字,这是我所听到的)由于比赛条件而发生的一些伏都教徒魔术,请始终在需要它的线程上获取/释放锁。

你怎么看?在这种统计上不可能的情况下处理比赛条件是否是一种良好的编程习惯?还是增加更多的代码行以降低可读性是完全没有必要,甚至会适得其反?


21
当人们说出这样的机会时,为什么没有人问到关于说出这个数字的人的教育呢?您需要先接受统计方面的正规教育,然后才能备份这样的数字。
Pieter B

27
作为物理学家,p <1E-140表示p = 0。在这个宇宙中不会发生。0.00000000000000000000000000000000000000000000000000000000000000001%大很多
MSalters

15
确保这种竞争状况不会导致某人故意崩溃您的应用程序。这可能是导致安全问题的原因。
toasted_flakes 2012年

27
百万分之一的机会发生十分之九。
卡兹龙

27
“几乎肯定没有机会发生吗?” 这意味着它会在凌晨3点在生产中发生,并且很可能非常昂贵。

Answers:


137

如果它确实是1 ^ 10 ^ 55中的事件,则无需为其编写代码。这意味着如果您每秒进行一百万次操作,则每3 * 10 ^ 41年就会收到一个错误,大约是宇宙年龄的10 ^ 31倍。如果您的应用程序每10万亿亿亿美元的年龄中只有一次错误,那可能就足够可靠了。

但是,我会非常打赌这个错误离不可能的程度还很远。如果您能想到该错误,几乎可以肯定至少会偶尔发生此错误,因此值得一开始就对其进行正确编码。另外,如果您在一开始就正确地对线程进行了编码,以使它们能够适当地获取和释放锁,那么将来这些代码将更易于维护。进行更改时,您不必担心,您不必重新分析所有潜在的竞争条件,重新计算它们的概率,并确保自己不会再次出现。


66
我想起了几年前读过的一则评论,但现在找不到“百万分之一的概率通常在下周二”。+1表示其“几乎不可能”。
Bevan 2012年

2
+1为下注。处理种族条件的最好方法是摆脱它们。
Blrfl 2012年

10
@Bevan“百万分之一的概率通常是下周二” ...除非您正在玩彩票:)
dasblinkenlight 2012年

22
@dasblinkenlight不过的机会有人在大多数彩票中奖接近100%。预测,这就是挑战。
Bevan 2012年

3
@Bevan:当我读到这个问题时,那句话正是我的想法-这里是参考:blogs.msdn.com/b/larryosterman/archive/2004/03/30/104165.aspx
Doc Brown

69

从成本效益的角度来看,只有在获得足够好处时,才应该编写其他代码。

例如,如果错误的线程“赢得比赛”而发生的最糟糕的事情是该信息将不会显示,并且用户需要单击“刷新”,则不必费心防范比赛条件:必须写很多代码不值得修复那些无关紧要的东西。

另一方面,如果竞争状况可能导致银行帐户之间不正确的汇款,那么无论您需要编写多少代码来解决此问题,您都必须避免竞争状况。


20
+1:用于区分“看起来像失败的失败”和“看起来像成功的失败”。取决于域,不正确的信息更为严重。
deworde 2012年

2
+1会导致比赛条件的结果有很大的不同。
格兰特(Grant)

+1竞赛条件的结果应成为决定是否解决的主要决定因素。可能导致飞机坠毁的竞赛条件与可能迫使用户重新打开应用程序的条件大不相同。
2012年

1
+1:我想说的是后果可能是您应该分析的内容,而不是其发生的可能性。如果后果无关紧要,即使非常常见,您也不必处理竞争条件。
Leo

1
但是不要以为自动解决竞争条件就意味着您必须编写更多代码。这也可能意味着删除大量的错误代码,并用较小的正确代码替换它。
JesperE 2012年

45

寻找比赛条件是困难的部分。您编写此问题的时间可能几乎与解决该问题所花费的时间差不多。并不是因为它使可读性降低了很多。程序员希望在这种情况下看到同步代码,实际上可能会浪费更多的时间来思考为什么不存在同步代码,以及添加同步代码是否可以解决他们无关的错误。

就概率而言,您会感到惊讶。去年,我收到了一份比赛状况错误报告,说我无法通过数千次自动尝试进行复制,但是一个系统由一个客户组成的系统始终可见。现在花5分钟修复它的商业价值,而不是对客户安装中的“不可能的”错误进行故障排除,使选择变得轻而易举。


1
这个也是!通过做必要的事情(即使它不太可能失败),避免让其他程序员在阅读代码时考虑可能的问题。
Casey Kuball 2012年

您的观点是正确的(现在进行的修复比以后进行的修复更快捷,更便宜),除了它永远不会是“立即修复5分钟”。
iconoclast

2
+1指出的竞争条件的概率可能取决于很多因素,所以即使它看起来不太可能在您的配置,可能更频繁地发生一个客户系统的/不同的OS /在下一版本等
sleske 2012年

27

获取并释放锁。概率改变,算法改变。这是一个坏习惯,当出现问题时,您不必停下来想知道自己是否错了……


6
+1用于算法更改。现在,当您知道竞争状况时,概率很低。一年后,当您忘记了竞争状况时,可以对代码进行更改,从而显着更改错误的时间和可能性。
Phil

13

而其他一些线程正在通过网络轮询数据或一定要花费5到10秒才能完成工作。

直到有人引入缓存层以提高性能。突然之间,其他胎面几乎瞬间完成,并且赛车状况更多地表现出来。

正是在几周前发生了这种情况,大约花了整整2天的开发人员才能发现该错误。

如果识别出竞赛条件,请务必对其进行修复。


8

简单与正确。

在许多情况下,简单性胜于正确性。这是一个成本问题。

此外,种族条件是令人讨厌的事情,往往不遵守简单的统计数据。一切正常,直到其他一些看似无关的同步导致您的比赛条件突然发生一半。当然,除非您打开日志或调试代码。

防止出现竞争状况(这可能很棘手)的一种实用的替代方法可以是检测并记录下来(对于艰难而过早的失败而言,这是奖金)。如果永远不会发生,您将损失很小。如果确实发生了,那么您就有充分的理由花额外的时间修复它。


1
+1记录日志,如果彻底修复它,则尽早失败。
马丁·巴

在许多情况下,简单性胜过完整性。在这些情况下,同步几乎从未发生过。稍后,它几乎总是会再次咬住您(或可怜的人,负责维护您的代码)。
reirab 2014年

@reirab我不同意。如果您考虑不经常发生的事件,则记录故障是具有成本效益的。例如:如果您的电话应用程序的故障率是1/100(崩溃),并且用户在确切的月份转换(1/31 23:59:00-> 2/1 00:00:00)切换网络,则您可能永远不会听说。但是,这样一来,服务器上的连接崩溃的可能性就只有1/10 ^ 9,这是不可接受的。这取决于。
ptyx

7

如果您的竞争条件与安全性有关,则应始终编写代码以防止这种情况。

一个常见的例子是在unix中创建/打开文件的竞争条件,如果运行竞争条件的程序以比用户与其交互的用户更高的特权运行,则在某些情况下,这可能导致特权升级攻击,例如系统守护进程或更糟糕的是,内核。

即使比赛条件具有随机发生10 ^(-80)的可能性,也很可能是确定的攻击者有相当大的机会故意和人为地创造这种条件。


6

Therac-25!

Therac-25项目的开发人员对X射线治疗机中UI和与界面相关的问题之间的时间安排很有信心。

他们不应该这样。

您可以通过以下网址了解有关这一著名的生死软件灾难的更多信息:

http://www.youtube.com/watch?v=izGSOsAGIVQ

要么

http://en.wikipedia.org/wiki/塞拉克25

您的应用程序对故障的敏感性可能不如医疗设备。一种有用的方法是将风险暴露评估为所有可能生产的产品在产品生命周期中的发生可能性和发生成本的乘积。

如果您选择构建代码以使代码持久(听起来像您已经做到了),则应考虑摩尔定律,因为随着系统内部或外部计算机的速度越来越快,摩尔定律可以轻松地每隔几年舍弃几个零。如果寄出数千份,则放弃更多的零。如果用户连续数年每天(或每月)执行此操作,则需要多花一些时间。如果在有Google光纤的地方使用它,那该怎么办?如果UI垃圾收集了GUI中途操作,这会影响比赛吗?您是在GUI后面使用开放源代码还是Windows库?那里的更新会影响时间吗?

信号量,锁,互斥量,屏障同步是在线程之间同步活动的方法之一。如果您不使用它们,则可能由另一个维护您的程序的人,然后很快就会对线程之间的关系进行假设,并且有关竞争条件的计算可能会失效。

我建议您显式同步,因为虽然您可能从未看到过同步问题,但客户可能会同步。此外,即使您的种族状况从未发生,如果您或您的组织被要求为您的代码辩护,也会发生什么(因为丰田几年前与普锐斯(Prius)息息相关)。您的方法学越全面,您的票价就会越高。说“我们谨防这种不太可能的情况……”比说“我们知道我们的代码将失败,但我们写下该等式表明它在我们的一生中不会发生。”可能更好。 ”

听起来概率计算来自其他人。他们是否了解您的代码,并且您足够了解他们以至于不会犯任何错误吗?如果我为某事计算出99.99997%的可靠性,我可能还会回想我的大学统计课,并记住我并不总是获得100%的收益,而根据我自己的个人可靠性估计却退缩了相当多的百分比。


1
+1提及Therac-25。这里有很多重要的教训。
斯图尔特·马克

虽然我认为这是一个很好的答案,但您可以辩称,如果您未能消除比赛条件,那么您的业余GUI项目肯定不会导致人们死亡。
marktani 2012年

我没有什么可争辩的,但是如果我是我,我可能会争辩说,无论何时我们编写代码,我们都应该正确编写。如果我们可以练习从代码更简单的业余项目中解脱出竞争条件,也许我们是唯一的作者,那么当我们处理需要将多个作者的工作整合在一起的工作项目时,我们会准备得更多。
DeveloperDon

4

增加更多的代码行来阻碍可读性是完全没有必要,甚至会适得其反吗?

简单只有在正确的情况下才是好的。由于此代码不正确,因此将来的程序员在寻找相关错误时不可避免地对其进行查看。

无论采用哪种方式处理(通过记录日志,对其进行记录或添加锁-这取决于成本),您都可以在查看代码时节省其他程序员的时间。


3

这将取决于上下文。如果它是一款休闲的iPhone游戏,可能不是。可能是下一个载人航天器的飞行控制系统。如果根据修复问题的估计成本来衡量是否出现“不良”结果,这完全取决于后果。

这些类型的问题很少有“一刀切”的答案,因为它们不是编程问题,而是经济学问题。


3
无疑是“下一架载人航天器的飞行控制系统” 。
deworde

可能……绝对……这取决于谁在火箭上:-)
GrandmasterB

3

是的,期待意外。我已经花了几个小时(用其他人的代码^^)查找永远不会发生的情况。

诸如总是有别的东西,总是有大小写默认值,初始化变量(是的,确实..由此发生的错误),检查循环中是否有每次迭代的重用变量之类的事情,等等。

如果您特别担心线程问题,请阅读有关该主题的博客,文章和书籍。当前的主题似乎是不可变的数据。


3

修复它。

我已经看到了。一个线程设法向服务器发出网络请求,该服务器执行复杂的数据库查找并在另一个线程到达下一行代码之前做出响应。它发生了。

某个地方的某个客户会决定某一天运行某种东西,而这会占用“快速”线程的所有CPU时间,而使慢速线程仍在运行,您会后悔的:)


1

如果您已经意识到不太可能出现的竞争情况,请至少在代码中记录下来!

编辑:我应该补充说,如果可能的话,我会修复它,但是在编写上述文件时,没有其他答案明确指出至少要在代码中记录问题。


1
是的,至少要尝试检测到并记录下来(如果发生)。恕我直言,避免一切错误完全可以。但是至少要让别人知道它发生了,并且您对它不会被误导的假设也知道了。
史蒂夫·贝内特

0

我认为,如果您已经知道如何以及为什么会发生这种情况,不妨处理一下。那就是如果它不占用大量资源。


0

这完全取决于比赛条件的后果。我认为回答您问题的人是正确的。我的是路由器配置引擎。对我来说,竞争条件使系统静止不动,损坏或未配置,即使它说成功了。我总是在每个路由器上使用信号灯,这样我就不必手工清理任何东西。

我认为我的某些GUI代码仍然很容易出现竞争状况,因为发生竞争状况可能会给用户一个错误,但是如果有数据损坏或行为不当的可能性,我将没有任何这种可能性。此类事件发生后申请。


0

有趣的是,我最近遇到了这个问题。在我的情况下,我什至没有意识到比赛条件是可能的。只有在多核处理器成为标准时,竞争条件才会显现出来。

场景大致是这样的。设备驱动程序引发了软件要处理的事件。控制必须尽快返回设备驱动程序,以防止设备超时。为了确保这一点,该事件被记录在一个单独的线程中并排队。

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

多年来效果很好。然后突然在某些配置下它将失败。事实证明,排队线程现在实际上与事件处理线程并行运行,而不是共享单个处理器的时间。在事件被确认之前,它设法将下一个命令发送到设备,从而导致顺序错误。

考虑到它只影响一种配置的一位客户,我可耻地将Thread.Sleep(1000)问题放在了哪里。此后没有问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.