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

我对社区的问题是:

竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?


当前回答

竞态条件是并发编程中的一种情况,其中两个并发线程或进程争夺资源,最终状态取决于谁先获得资源。

其他回答

竞态条件是并发编程中的一种情况,其中两个并发线程或进程争夺资源,最终状态取决于谁先获得资源。

当访问共享资源的多线程(或其他并行)代码可能以导致意外结果的方式访问共享资源时,就存在“竞争条件”。

举个例子:

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值(因为还没有线程更改它),然后它们都将在x中存储相同的值(x+1) !

例子:

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
}

这里,答案每次都是50,000,000。

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

这个讨论中的许多答案解释了什么是竞态条件。我试图解释为什么这个术语在软件行业被称为竞争条件。

为什么叫竞态条件?

竞态条件不仅与软件有关,也与硬件有关。实际上,这个术语最初是由硬件行业创造的。

根据维基百科:

这个术语起源于两个信号相互竞争的想法 首先影响输出。 逻辑电路中的竞态:

软件行业没有对这个术语进行修改,这使得它有点难以理解。

你需要做一些替换来把它映射到软件世界:

"两个信号" ==> "两个线程"/"两个进程" "影响输出" ==> "影响一些共享状态"

因此,软件行业中的竞争条件是指“两个线程”/“两个进程”相互竞争以“影响某种共享状态”,而共享状态的最终结果将取决于一些微妙的时间差,而时间差可能是由某些特定的线程/进程启动顺序、线程/进程调度等引起的。

什么是竞态条件?

过程严重依赖于其他事件的顺序或时间的情况。

例如, 处理器A和处理器B的执行都需要相同的资源。

你如何发现它们?

有一些工具可以自动检测竞态状态:

基于锁集的竞赛检查器 发生在种族检测之前 杂交种族检测

你是如何处理的?

竞争条件可以由互斥量或信号量处理。它们就像锁一样,允许进程根据特定的需求获取资源,以防止竞争。

你如何防止它们的发生?

防止竞争状态的方法有很多种,比如避免临界区。

没有两个进程同时在它们的关键区域内。(互斥) 没有对速度或cpu数量做任何假设。 没有进程运行在阻塞其他进程的关键区域之外。 没有进程需要永远等待才能进入临界区。(A等待B资源,B等待C资源,C等待A资源)

我做了一个视频来解释这个。

从本质上讲,它是当你有一个跨多个线程共享的状态,在一个给定状态的第一次执行完成之前,另一个执行开始,一个给定操作的新线程的初始状态是错误的,因为前一次执行还没有完成。

由于第二次执行的初始状态是错误的,因此计算结果也是错误的。因为最终第二次执行会用错误的结果更新最终状态。

你可以在这里查看。 https://youtu.be/RWRicNoWKOY