在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
当前回答
竞态条件是当两个或多个进程可以同时访问和更改共享数据时出现的不希望出现的情况。这是因为对资源的访问发生冲突。临界区问题可能导致竞争状态。为了解决进程之间的临界条件,我们每次只取出一个执行临界段的进程。
其他回答
竞态条件是一种bug,只会在特定的时间条件下发生。
例子: 假设您有两个线程,A和B。
在线程A中:
if( object.a != 0 )
object.avg = total / object.a
线程B:
object.a = 0
如果线程A在检查完对象后被抢占。a不为空,B将执行a = 0,当线程a将获得处理器时,它将执行“除零”。
这个错误只发生在if语句之后的线程A被抢占时,这是非常罕见的,但它是有可能发生的。
我做了一个视频来解释这个。
从本质上讲,它是当你有一个跨多个线程共享的状态,在一个给定状态的第一次执行完成之前,另一个执行开始,一个给定操作的新线程的初始状态是错误的,因为前一次执行还没有完成。
由于第二次执行的初始状态是错误的,因此计算结果也是错误的。因为最终第二次执行会用错误的结果更新最终状态。
你可以在这里查看。 https://youtu.be/RWRicNoWKOY
当两个或多个线程可以访问共享数据,并且它们试图同时更改数据时,就会发生竞态条件。因为线程调度算法可以在任何时候在线程之间交换,所以您不知道线程将尝试访问共享数据的顺序。因此,数据更改的结果依赖于线程调度算法,即两个线程都在“竞相”访问/更改数据。
当一个线程执行“检查-然后-行动”时,问题经常发生。“check”如果值是X,那么“act”做一些取决于值是X的事情),另一个线程在“check”和“act”之间对值做一些事情。例句:
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
如果你使用“原子”类,你可以防止竞争条件。原因是线程没有分开操作get和set,示例如下:
AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);
因此,你将有7在链接“ai”。 虽然你做了两个操作,但这两个操作都确认了同一个线程,没有其他线程会干涉这,这意味着没有竞争条件!
当访问共享资源的多线程(或其他并行)代码可能以导致意外结果的方式访问共享资源时,就存在“竞争条件”。
举个例子:
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。
有关锁的更多信息,请搜索:互斥量,信号量,临界区,共享资源。