在编写多线程应用程序时,遇到的最常见的问题之一是竞赛条件。
我向社区提出的问题是。
什么是竞赛条件?你如何检测它们?你如何处理它们?最后,你如何防止它们的发生?
当两个或更多的线程可以访问共享数据,并且它们试图同时改变它时,就会出现竞赛条件。因为线程调度算法可以随时在线程之间进行交换,你不知道线程试图访问共享数据的顺序。因此,数据变化的结果取决于线程调度算法,即两个线程都在"比赛"访问/改变数据。
当一个线程做一个"检查-然后-行动"(例如"检查"如果值是X,然后"行动"做一些取决于值是X的事情),而另一个线程在"检查"和"行动"之间对值做一些事情时,问题经常发生。举例来说。
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
关键是,y可能是10,也可能是任何东西,这取决于另一个线程在检查和行动之间是否改变了x。你没有真正的办法知道。
为了防止竞赛条件的发生,你通常会在共享数据周围加一个锁,以确保在同一时间只有一个线程可以访问这些数据。这意味着像这样的事情。
// Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
竞赛条件是一种错误,只在某些时间条件下发生。
举个例子。 假设你有两个线程,A和B。
在线程A中
if( object.a != 0 )
object.avg = total / object.a
在线程B中。
object.a = 0
如果线程A在检查完object.a不是null后被抢占,B会做a = 0
,当线程A获得处理器后,它会做一个"除以0"。
这个错误只发生在线程A在if语句后被抢占的情况下,这非常罕见,但它可能发生。
竞赛条件是并发编程中的一种情况,即两个并发的线程或进程竞争一个资源,所产生的最终状态取决于谁先获得该资源。