什么是竞赛条件?

在编写多线程应用程序时,遇到的最常见问题之一就是竞争条件。

我对社区的问题是:

什么是竞赛条件? 你如何检测它们? 你如何处理它们? 最后,你如何防止它们发生?


当两个或更多线程可以访问共享数据并尝试同时更改共享数据时,会发生争用情况。 因为线程调度算法可以随时在线程之间交换,所以您不知道线程尝试访问共享数据的顺序。 因此,数据更改的结果取决于线程调度算法,即两个线程都“竞争”以访问/更改数据。

当一个线程执行“check-then-act”时会出现问题(例如,如果值为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

当访问共享资源的多线程(或其他并行)代码可能会导致意外结果时,会出现“竞态条件”。

以这个例子:

for ( int i = 0; i < 10000000; i++ )
{
   x = x + 1; 
}

如果你有5个线程一次执行这个代码,x的值不会最终为50,000,000。 实际上,每次运行都会有所不同。

这是因为,为了让每个线程增加x的值,他们必须执行以下操作:(显然,简化)

Retrieve the value of x
Add 1 to this value
Store this value to x

任何线程都可以随时在此进程中的任何一个步骤,并且在涉及共享资源时可以相互进行。 在x被读取和写回之间的时间内,x的状态可以被另一个线程改变。

假设一个线程检索x的值,但还没有存储它。 另一个线程也可以检索x的相同值(因为没有线程改变了它),然后它们都会将相同的值(x + 1)存回x!

例:

Thread 1: reads x, value is 7
Thread 1: add 1 to x, value is now 8
Thread 2: reads x, value is 7
Thread 1: stores 8 in x
Thread 2: adds 1 to x, value is now 8
Thread 2: stores 8 in x

竞争条件可以通过在访问共享资源的代码之前采用某种锁定机制来避免:

for ( int i = 0; i < 10000000; i++ )
{
   //lock x
   x = x + 1; 
   //unlock x
}

在这里,答案每次都是5000万。

有关锁定的更多信息,请搜索:互斥体,信号量,临界区域,共享资源。


什么是种族条件?

你打算在下午5点去看电影。 您在下午4点询问门票的可用性。 该代表说他们可用。 放松并在演出前5分钟到达售票窗口。 我相信你可以猜到会发生什么:这是一个完整的房子。 问题在于检查和行动之间的持续时间。 你在4点询问并采取行动5.同时,别人抓住了票。 这是一种竞赛条件 - 特别是竞赛条件下的“随后行为”。

你如何检测它们?

宗教代码审查,多线程单元测试。 没有捷径。 有几个Eclipse插件正在出现,但没有稳定。

你如何处理和预防他们?

最好的做法是创建副作用的自由和无状态函数,尽可能使用不可变项。 但这并不总是可能的。 因此,使用java.util.concurrent.atomic,并发数据结构,适当的同步和基于actor的并发将会有所帮助。

并发的最佳资源是JCIP。 你也可以在这里获得更多关于上述解释的细节。

链接地址: http://www.djcxy.com/p/51245.html

上一篇: What is a race condition?

下一篇: Closure Because of What it Can Do or Because it Does