在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
在编写多线程应用程序时,遇到的最常见的问题之一是竞争条件。
我对社区的问题是:
竞态条件是什么? 你如何发现它们? 你是如何处理的? 最后,你如何防止它们的发生?
当前回答
如果你使用“原子”类,你可以防止竞争条件。原因是线程没有分开操作get和set,示例如下:
AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);
因此,你将有7在链接“ai”。 虽然你做了两个操作,但这两个操作都确认了同一个线程,没有其他线程会干涉这,这意味着没有竞争条件!
其他回答
考虑一个操作,该操作必须在计数增加时立即显示计数。ie。,只要CounterThread增加值,DisplayThread就需要显示最近更新的值。
int i = 0;
输出
CounterThread -> i = 1
DisplayThread -> i = 1
CounterThread -> i = 2
CounterThread -> i = 3
CounterThread -> i = 4
DisplayThread -> i = 4
在这里,CounterThread频繁地获取锁,并在DisplayThread显示它之前更新值。这里存在一个竞态条件。竞争条件可以通过使用同步来解决
Race conditions occur in multi-threaded applications or multi-process systems. A race condition, at its most basic, is anything that makes the assumption that two things not in the same thread or process will happen in a particular order, without taking steps to ensure that they do. This happens commonly when two threads are passing messages by setting and checking member variables of a class both can access. There's almost always a race condition when one thread calls sleep to give another thread time to finish a task (unless that sleep is in a loop, with some checking mechanism).
防止竞争条件的工具依赖于语言和操作系统,但一些常见的工具是互斥锁、临界区和信号。互斥锁在你想确保你是唯一一个在做某事的时候很有用。当你想确保别人已经完成某件事时,信号是很好的。最小化共享资源还有助于防止意外行为
Detecting race conditions can be difficult, but there are a couple signs. Code which relies heavily on sleeps is prone to race conditions, so first check for calls to sleep in the affected code. Adding particularly long sleeps can also be used for debugging to try and force a particular order of events. This can be useful for reproducing the behavior, seeing if you can make it disappear by changing the timing of things, and for testing solutions put in place. The sleeps should be removed after debugging.
但是,如果某个问题只在某些机器上断断续续地发生,则是存在竞争条件的标志性标志。常见的错误是崩溃和死锁。使用日志记录,您应该能够找到受影响的区域并从那里返回。
竞态条件是并发编程中的一种情况,其中两个并发线程或进程争夺资源,最终状态取决于谁先获得资源。
在竞争条件和数据竞争之间有一个重要的技术差异。大多数答案似乎假设这些术语是等价的,但事实并非如此。
当两条指令访问相同的内存位置时,就会发生数据竞争,其中至少有一个是写,并且在这些访问之间没有发生排序之前。现在,什么构成happens before顺序还存在很多争论,但一般来说,同一个锁变量上的ulock-lock对和同一个条件变量上的wait-signal对诱导的是happens before顺序。
竞态条件是语义错误。它是发生在事件的时间或顺序上的缺陷,会导致错误的程序行为。
许多竞态条件可能(事实上也是)是由数据竞态引起的,但这是不必要的。事实上,数据竞争和竞争条件既不是彼此的必要条件,也不是彼此的充分条件。这篇博客文章也用一个简单的银行交易例子很好地解释了两者的区别。下面是另一个简单的例子来解释这种区别。
既然我们已经确定了术语,让我们试着回答最初的问题。
由于竞争条件是语义错误,因此没有检测它们的通用方法。这是因为在一般情况下,不可能有一个自动的oracle来区分正确和不正确的程序行为。种族检测是一个无法确定的问题。
另一方面,数据竞争有一个精确的定义,它不一定与正确性有关,因此可以检测到它们。数据竞争检测器有很多种(静态/动态数据竞争检测、基于锁集的数据竞争检测、基于先于事件发生的数据竞争检测、混合数据竞争检测)。最先进的动态数据竞争检测器是ThreadSanitizer,它在实践中工作得非常好。
处理数据竞争通常需要一些编程规程来在访问共享数据之间的边之前诱导happens(在开发过程中,或者在使用上述工具检测到它们之后)。这可以通过锁、条件变量、信号量等来实现。但是,还可以使用不同的编程范例,例如消息传递(而不是共享内存),以避免构造造成的数据竞争。
我做了一个视频来解释这个。
从本质上讲,它是当你有一个跨多个线程共享的状态,在一个给定状态的第一次执行完成之前,另一个执行开始,一个给定操作的新线程的初始状态是错误的,因为前一次执行还没有完成。
由于第二次执行的初始状态是错误的,因此计算结果也是错误的。因为最终第二次执行会用错误的结果更新最终状态。
你可以在这里查看。 https://youtu.be/RWRicNoWKOY