什么是竞争条件?

发布于 2024-07-04 02:59:37 字数 146 浏览 7 评论 0原文

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

我向社区提出的问题是:

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

When writing multithreaded applications, one of the most common problems experienced is race conditions.

My questions to the community are:

  • What is the race condition?
  • How do you detect them?
  • How do you handle them?
  • Finally, how do you prevent them from occurring?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

摇划花蜜的午后 2024-07-11 02:59:37

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

举个例子:

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+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
}

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

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

A "race condition" exists when multithreaded (or otherwise parallel) code that would access a shared resource could do so in such a way as to cause unexpected results.

Take this example:

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

If you had 5 threads executing this code at once, the value of x WOULD NOT end up being 50,000,000. It would in fact vary with each run.

This is because, in order for each thread to increment the value of x, they have to do the following: (simplified, obviously)

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

Any thread can be at any step in this process at any time, and they can step on each other when a shared resource is involved. The state of x can be changed by another thread during the time between x is being read and when it is written back.

Let's say a thread retrieves the value of x, but hasn't stored it yet. Another thread can also retrieve the same value of x (because no thread has changed it yet) and then they would both be storing the same value (x+1) back in x!

Example:

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

Race conditions can be avoided by employing some sort of locking mechanism before the code that accesses the shared resource:

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

Here, the answer comes out as 50,000,000 every time.

For more on locking, search for: mutex, semaphore, critical section, shared resource.

当梦初醒 2024-07-11 02:59:37

当两个或多个线程可以访问共享数据并且它们试图同时更改它时,就会出现竞争条件。 由于线程调度算法可以随时在线程之间交换,因此您不知道线程尝试访问共享数据的顺序。 因此,数据改变的结果取决于线程调度算法,即两个线程“竞争”访问/改变数据。

当一个线程执行“检查然后执行”操作(例如“检查”值是否为 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 race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are "racing" to access/change the data.

Problems often occur when one thread does a "check-then-act" (e.g. "check" if the value is X, then "act" to do something that depends on the value being X) and another thread does something to the value in between the "check" and the "act". E.g:

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.
}

The point being, y could be 10, or it could be anything, depending on whether another thread changed x in between the check and act. You have no real way of knowing.

In order to prevent race conditions from occurring, you would typically put a lock around the shared data to ensure only one thread can access the data at a time. This would mean something like this:

// 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
街道布景 2024-07-11 02:59:37

什么是竞争条件?

你计划下午 5 点去看电影。 您在下午 4 点询问是否有票。 代表说他们可以。 您放松身心,在演出前 5 分钟到达售票窗口。 我相信你能猜到会发生什么:满座。 这里的问题在于检查和操作之间的持续时间。 你4点询价,5点行动,同时,有人抢了票。 这是一个竞争条件——特别是竞争条件的“检查然后行动”场景。

如何检测它们?

宗教代码审查、多线程单元测试。 没有捷径。 很少有 Eclipse 插件出现在这方面,但还没有稳定的。

你如何处理和预防它们?

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

并发的最佳资源是 JCIP。 您还可以此处获取有关上述说明的更多详细信息

What is a Race Condition?

You are planning to go to a movie at 5 pm. You inquire about the availability of the tickets at 4 pm. The representative says that they are available. You relax and reach the ticket window 5 minutes before the show. I'm sure you can guess what happens: it's a full house. The problem here was in the duration between the check and the action. You inquired at 4 and acted at 5. In the meantime, someone else grabbed the tickets. That's a race condition - specifically a "check-then-act" scenario of race conditions.

How do you detect them?

Religious code review, multi-threaded unit tests. There is no shortcut. There are few Eclipse plugin emerging on this, but nothing stable yet.

How do you handle and prevent them?

The best thing would be to create side-effect free and stateless functions, use immutables as much as possible. But that is not always possible. So using java.util.concurrent.atomic, concurrent data structures, proper synchronization, and actor based concurrency will help.

The best resource for concurrency is JCIP. You can also get some more details on above explanation here.

且行且努力 2024-07-11 02:59:37

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

A race condition is a situation on concurrent programming where two concurrent threads or processes compete for a resource and the resulting final state depends on who gets the resource first.

娇柔作态 2024-07-11 02:59:37

竞争条件和数据竞争之间存在重要的技术差异。 大多数答案似乎都假设这些术语是等价的,但事实并非如此。

当 2 条指令访问同一内存位置时,就会发生数据竞争,这些访问中至少有一个是写入,并且这些访问之间在排序之前不会发生。 现在,什么构成发生在排序之前存在很多争论,但一般来说,同一锁定变量上的 ulock-lock 对和同一条件变量上的等待信号对会导致发生在排序之前。

竞争条件是语义错误。 事件的时间或顺序中出现的缺陷会导致错误的程序行为

许多竞争条件可能(事实上确实是)由数据竞争引起,但这不是必要的。 事实上,数据竞争和竞争条件既不是彼此的必要条件,也不是充分条件。 这篇博文还通过一个简单的银行交易示例很好地解释了这种差异。 这是另一个简单的解释差异的示例

现在我们已经确定了术语,让我们尝试回答最初的问题。

鉴于竞争条件是语义错误,因此没有通用的方法来检测它们。 这是因为在一般情况下没有办法有一个自动化的预言机可以区分正确与不正确的程序行为。 种族检测是一个无法判定的问题。

另一方面,数据竞争有一个精确的定义,不一定与正确性相关,因此可以检测到它们。 数据竞争检测器有多种类型(静态/动态数据竞争检测、基于锁集的数据竞争检测、基于发生之前的数据竞争检测、混合数据竞争检测)。 最先进的动态数据竞争检测器是 ThreadSanitizer,它可以工作在实践中非常好。

处理数据竞争通常需要一些编程规则来在共享数据访问之间引入发生前边缘(无论是在开发期间,还是在使用上述工具检测到它们之后)。 这可以通过锁、条件变量、信号量等来完成。但是,也可以采用不同的编程范例,例如消息传递(而不是共享内存),通过构造来避免数据竞争。

There is an important technical difference between race conditions and data races. Most answers seem to make the assumption that these terms are equivalent, but they are not.

A data race occurs when 2 instructions access the same memory location, at least one of these accesses is a write and there is no happens before ordering among these accesses. Now what constitutes a happens before ordering is subject to a lot of debate, but in general ulock-lock pairs on the same lock variable and wait-signal pairs on the same condition variable induce a happens-before order.

A race condition is a semantic error. It is a flaw that occurs in the timing or the ordering of events that leads to erroneous program behavior.

Many race conditions can be (and in fact are) caused by data races, but this is not necessary. As a matter of fact, data races and race conditions are neither the necessary, nor the sufficient condition for one another. This blog post also explains the difference very well, with a simple bank transaction example. Here is another simple example that explains the difference.

Now that we nailed down the terminology, let us try to answer the original question.

Given that race conditions are semantic bugs, there is no general way of detecting them. This is because there is no way of having an automated oracle that can distinguish correct vs. incorrect program behavior in the general case. Race detection is an undecidable problem.

On the other hand, data races have a precise definition that does not necessarily relate to correctness, and therefore one can detect them. There are many flavors of data race detectors (static/dynamic data race detection, lockset-based data race detection, happens-before based data race detection, hybrid data race detection). A state of the art dynamic data race detector is ThreadSanitizer which works very well in practice.

Handling data races in general requires some programming discipline to induce happens-before edges between accesses to shared data (either during development, or once they are detected using the above mentioned tools). this can be done through locks, condition variables, semaphores, etc. However, one can also employ different programming paradigms like message passing (instead of shared memory) that avoid data races by construction.

马蹄踏│碎落叶 2024-07-11 02:59:37

竞争条件是一种错误,仅在某些时间条件下才会发生。

例子:
假设您有两个线程 A 和 B。

在线程 A 中:

if( object.a != 0 )
    object.avg = total / object.a

在线程 B 中:

object.a = 0

如果线程 A 在检查 object.a 不为 null 后立即被抢占,则 B 将执行 a = 0,并且当线程 A 获得处理器时,它将执行“除以零”。

此错误仅在线程 A 在 if 语句之后被抢占时发生,这种情况非常罕见,但有可能发生。

A race condition is a kind of bug, that happens only with certain temporal conditions.

Example:
Imagine you have two threads, A and B.

In Thread A:

if( object.a != 0 )
    object.avg = total / object.a

In Thread B:

object.a = 0

If thread A is preempted just after having check that object.a is not null, B will do a = 0, and when thread A will gain the processor, it will do a "divide by zero".

This bug only happen when thread A is preempted just after the if statement, it's very rare, but it can happen.

心如狂蝶 2024-07-11 02:59:37

一种规范的定义是“当两个线程同时访问内存中的同一位置,并且至少其中一次访问是写入”。 在这种情况下,“读取器”线程可能会获取旧值或新值,具体取决于哪个线程“赢得了比赛”。 这并不总是一个错误——事实上,一些非常复杂的低级算法故意这样做——但通常应该避免。 @Steve Gury 给出了一个很好的例子,说明什么时候可能会出现问题。

A sort-of-canonical definition is "when two threads access the same location in memory at the same time, and at least one of the accesses is a write." In the situation the "reader" thread may get the old value or the new value, depending on which thread "wins the race." This is not always a bug—in fact, some really hairy low-level algorithms do this on purpose—but it should generally be avoided. @Steve Gury give's a good example of when it might be a problem.

始终不够爱げ你 2024-07-11 02:59:37

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

为什么称为竞争条件?

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

根据维基百科

该术语起源于两个信号相互竞争的想法
首先影响输出

逻辑电路中的竞争条件:

在此输入图像描述

软件行业未经修改就采用了这个术语,这使得它有点难以理解。

您需要做一些替换才能将其映射到软件世界:

  • “两个信号”==> “两个线程”/“两个进程”
  • “影响输出”==> “影响某些共享状态”

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

Many answers in this discussion explains what a race condition is. I try to provide an explaination why this term is called race condition in software industry.

Why is it called race condition?

Race condition is not only related with software but also related with hardware too. Actually the term was initially coined by the hardware industry.

According to wikipedia:

The term originates with the idea of two signals racing each other to
influence the output first.

Race condition in a logic circuit:

enter image description here

Software industry took this term without modification, which makes it a little bit difficult to understand.

You need to do some replacement to map it to the software world:

  • "two signals" ==> "two threads"/"two processes"
  • "influence the output" ==> "influence some shared state"

So race condition in software industry means "two threads"/"two processes" racing each other to "influence some shared state", and the final result of the shared state will depend on some subtle timing difference, which could be caused by some specific thread/process launching order, thread/process scheduling, etc.

顾冷 2024-07-11 02:59:37

竞争条件发生在多线程应用程序或多进程系统中。 最基本的竞争条件是假设不在同一线程或进程中的两件事情将以特定顺序发生,而不采取措施确保它们确实发生的任何情况。 当两个线程通过设置和检查都可以访问的类的成员变量来传递消息时,通常会发生这种情况。 当一个线程调用 sleep 来给另一个线程时间来完成任务时,几乎总是存在竞争条件(除非 sleep 处于循环中,并具有某种检查机制)。

防止竞争条件的工具取决于语言和操作系统,但一些常见的工具是互斥体、临界区和信号。 当你想确保你是唯一一个在做某事的人时,互斥锁是很好的选择。 当您想确保其他人完成某件事时,信号是很好的。 最大限度地减少共享资源还有助于防止意外行为

检测竞争条件可能很困难,但有一些迹象。 严重依赖睡眠的代码很容易出现竞争条件,因此首先检查受影响代码中对睡眠的调用。 添加特别长的睡眠也可用于调试,以尝试强制执行特定的事件顺序。 这对于重现行为、查看是否可以通过改变事情的时间安排使其消失以及测试已实施的解决方案很有用。 调试后应删除睡眠。

不过,存在竞争条件的标志是,是否存在仅在某些机器上间歇性发生的问题。 常见的错误是崩溃和死锁。 通过日志记录,您应该能够找到受影响的区域并从那里返回工作。

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).

Tools for preventing race conditions are dependent on the language and OS, but some comon ones are mutexes, critical sections, and signals. Mutexes are good when you want to make sure you're the only one doing something. Signals are good when you want to make sure someone else has finished doing something. Minimizing shared resources can also help prevent unexpected behaviors

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.

The signature sign that one has a race condition though, is if there's an issue that only occurs intermittently on some machines. Common bugs would be crashes and deadlocks. With logging, you should be able to find the affected area and work back from there.

瞄了个咪的 2024-07-11 02:59:37

事实上,微软已经就竞争条件和死锁问题发表了一篇非常详细的文章。 其中最概括的摘要是标题段落:

当两个线程访问共享变量时,会发生竞争条件
同一时间。 第一个线程读取变量,第二个线程读取变量
线程从变量中读取相同的值。 然后是第一个线程
第二个线程对值执行操作,并且它们竞争
查看哪个线程可以将值最后写入共享变量。
最后写入其值的线程的值被保留,
因为该线程正在重写前一个线程的值
写道。

Microsoft actually have published a really detailed article on this matter of race conditions and deadlocks. The most summarized abstract from it would be the title paragraph:

A race condition occurs when two threads access a shared variable at
the same time. The first thread reads the variable, and the second
thread reads the same value from the variable. Then the first thread
and second thread perform their operations on the value, and they race
to see which thread can write the value last to the shared variable.
The value of the thread that writes its value last is preserved,
because the thread is writing over the value that the previous thread
wrote.

时光是把杀猪刀 2024-07-11 02:59:37

什么是竞争条件?

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

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

如何检测它们?

有一些工具可以自动检测竞争条件:

你如何处理它们?

竞争条件可以通过互斥量信号量来处理。 它们充当锁,允许进程根据某些要求获取资源,以防止竞争条件。

如何防止它们发生?

有多种方法可以防止竞争情况,例如避免临界区

  1. 没有两个进程同时位于其临界区域内。 (互斥)
  2. 不对速度或 CPU 数量做出任何假设。
  3. 没有进程在其临界区域之外运行而阻塞其他进程。
  4. 没有进程必须永远等待才能进入其临界区域。 (A等待B资源,B等待C资源,C等待A资源)

What is a race condition?

The situation when the process is critically dependent on the sequence or timing of other events.

For example,
Processor A and processor B both needs identical resource for their execution.

How do you detect them?

There are tools to detect race condition automatically:

How do you handle them?

Race condition can be handled by Mutex or Semaphores. They act as a lock allows a process to acquire a resource based on certain requirements to prevent race condition.

How do you prevent them from occurring?

There are various ways to prevent race condition, such as Critical Section Avoidance.

  1. No two processes simultaneously inside their critical regions. (Mutual Exclusion)
  2. No assumptions are made about speeds or the number of CPUs.
  3. No process running outside its critical region which blocks other processes.
  4. No process has to wait forever to enter its critical region. (A waits for B resources, B waits for C resources, C waits for A resources)
你在看孤独的风景 2024-07-11 02:59:37

我制作了一个视频来解释这一点。

本质上,当您有一个在多个线程之间共享的状态时,在给定状态的第一次执行完成之前,另一个执行开始,并且给定操作的新线程的初始状态是错误的,因为先前的执行尚未完成。

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

正在阅读共享状态

最终计算不正确

您可以在此处查看。
https://youtu.be/RWRicNoWKOY

I made a video that explains this.

Essentially it is when you have a state with is shared across multiple threads and before the first execution on a given state is completed, another execution starts and the new thread’s initial state for a given operation is wrong because the previous execution has not completed.

Because the initial state of the second execution is wrong, the resulting computation is also wrong. Because eventually the second execution will update the final state with the wrong result.

reading a shared state

final computation is incorrect

You can view it here.
https://youtu.be/RWRicNoWKOY

木森分化 2024-07-11 02:59:37

如果您使用“原子”类,您可以防止竞争条件。 原因只是线程没有分开操作 get 和 set,示例如下:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

结果,链接“ai”中将有 7。
虽然您做了两个操作,但是这两个操作都确认同一个线程,并且没有其他线程会干扰这一点,这意味着没有竞争条件!

You can prevent race condition, if you use "Atomic" classes. The reason is just the thread don't separate operation get and set, example is below:

AtomicInteger ai = new AtomicInteger(2);
ai.getAndAdd(5);

As a result, you will have 7 in link "ai".
Although you did two actions, but the both operation confirm the same thread and no one other thread will interfere to this, that means no race conditions!

上面给出的答案足以从技术上解释什么是竞争条件。

我只是想从外行的术语/角度添加解释:

“竞赛条件”就像一场涉及超过 1 名参与者的比赛。 谁先到达终点线,谁就是唯一的胜利者。 我们假设参与者获胜的机会是平等的。 假设比赛重复了不止一次。 所以我们无法准确预测每场比赛谁将成为获胜者。 每场比赛总有不同的获胜者潜力获胜。

问题就在这里 - 如果终点线是一个资源,而参与者是一个过程,那么可能不同的过程将在每场比赛结束时到达资源并成为获胜者。

涉及竞争条件的问题是,如果进程 A 在“竞赛”开始时更改了值,则不能保证进程 A 最终(终点线)再次达到资源中的相同值,因为 A 可能会输掉比赛。 如果其他进程(例如 B)成为获胜者,则 B可能在进程 A 到达之前更改 A 设置的值。 如果发生这种情况,进程 A 就会失去其价值,并将给进程 A 带来问题。

因此,“竞争条件”问题是进程潜在从共享资源中失去其价值,这是由其他进程的修改引起的过程。 则竞争条件不是问题。

  1. 如果没有发生更改/更新,或者
  2. 同一进程能够重新获得并控制其值,或者
  3. 不涉及共享资源,

竞争条件问题可以通过添加“保证”来解决,即当进程正在使用共享资源(读或写)时,没有其他进程可以访问共享资源。 保证的时间段称为“关键部分”。

可以通过创建锁来提供这种保证。 例如,如果一个进程需要使用共享资源,它可以锁定资源并在使用完毕后释放它,如下所示的步骤。 锁可以使用称为信号量或互斥体的机制。 同时其他需要使用共享资源的进程也会执行相同的步骤。

wait until Mutex is unlocked
set Mutex=lock
begin read/write value in shared resource
..... do something
finish read/write value in shared resource
set Mutex=unlock

这样,进程 A 可以确保在 A 使用资源时,没有其他进程会更新共享资源。 同样的问题也适用于线程。

The answers given above are excellent enough to explain technically what is race condition.

I just want to add explanation from a layman's term/perspective:

'Race condition' is like in a race which involves more than 1 participant. Whoever reach the finishing line first is the only winner. We assume chances of winning are equal among participants. Let say the race is repeated more than once. So we can't predict exactly who will be the winner in each race. There is always a potential that different winner will win in each race.

Here where the problem comes in - If the finishing line is a resource, and a participant is a process, potentially different process will reach the resource at the end of every race and become a winner.

The problem involving race condition is that if process A changed the value in the beginning of 'race', it is not guaranteed that process A will reach the same value in the resource again in the end (finishing line), since A potentially might lose the race. If other process e.g. B become the winner, then B may change the value set by A before process A reach it. If this happened, process A lose its value and will cause problem to process A.

So issue with 'race condition' is the potential of a process lost its value from shared resource, caused by the modification by other process. Race condition is not a problem/issue, if

  1. no change/update happened, or
  2. if the same process able to regain and control its value, or
  3. no sharing resource involved.

Problem with race condition can be solved by adding an 'assurance' that no other process can access the shared resource while a process is using it (read or write). The period of time for the assurance is called the 'critical section'.

This assurance can be provided by creating a lock. E.g. If a process need to use a shared resource, it can lock the resource and release it when it is done, as the steps shown below. The lock may use the mechanism called Semaphore or Mutex. Meanwhile other process that need to use the shared resource will do the same steps.

wait until Mutex is unlocked
set Mutex=lock
begin read/write value in shared resource
..... do something
finish read/write value in shared resource
set Mutex=unlock

This way a process A can ensure no other process will update the shared resource while A is using the resource. The same issue will apply for thread.

情场扛把子 2024-07-11 02:59:37

这是经典的银行账户余额示例,它将帮助新手轻松理解 Java 中的线程和竞争条件:

public class BankAccount {

/**
 * @param args
 */
int accountNumber;
double accountBalance;

public synchronized boolean Deposit(double amount){
    double newAccountBalance=0;
    if(amount<=0){
        return false;
    }
    else {
        newAccountBalance = accountBalance+amount;
        accountBalance=newAccountBalance;
        return true;
    }

}
public synchronized boolean Withdraw(double amount){
    double newAccountBalance=0;
    if(amount>accountBalance){
        return false;
    }
    else{
        newAccountBalance = accountBalance-amount;
        accountBalance=newAccountBalance;
        return true;
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    BankAccount b = new BankAccount();
    b.accountBalance=2000;
    System.out.println(b.Withdraw(3000));

}

Here is the classical Bank Account Balance example which will help newbies to understand Threads in Java easily w.r.t. race conditions:

public class BankAccount {

/**
 * @param args
 */
int accountNumber;
double accountBalance;

public synchronized boolean Deposit(double amount){
    double newAccountBalance=0;
    if(amount<=0){
        return false;
    }
    else {
        newAccountBalance = accountBalance+amount;
        accountBalance=newAccountBalance;
        return true;
    }

}
public synchronized boolean Withdraw(double amount){
    double newAccountBalance=0;
    if(amount>accountBalance){
        return false;
    }
    else{
        newAccountBalance = accountBalance-amount;
        accountBalance=newAccountBalance;
        return true;
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    BankAccount b = new BankAccount();
    b.accountBalance=2000;
    System.out.println(b.Withdraw(3000));

}
狼亦尘 2024-07-11 02:59:37

尝试这个基本示例以更好地理解竞争条件:

    public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}

Try this basic example for better understanding of race condition:

    public class ThreadRaceCondition {

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Account myAccount = new Account(22222222);

        // Expected deposit: 250
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.DEPOSIT, 5.00);
            t.start();
        }

        // Expected withdrawal: 50
        for (int i = 0; i < 50; i++) {
            Transaction t = new Transaction(myAccount,
                    Transaction.TransactionType.WITHDRAW, 1.00);
            t.start();

        }

        // Temporary sleep to ensure all threads are completed. Don't use in
        // realworld :-)
        Thread.sleep(1000);
        // Expected account balance is 200
        System.out.println("Final Account Balance: "
                + myAccount.getAccountBalance());

    }

}

class Transaction extends Thread {

    public static enum TransactionType {
        DEPOSIT(1), WITHDRAW(2);

        private int value;

        private TransactionType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    };

    private TransactionType transactionType;
    private Account account;
    private double amount;

    /*
     * If transactionType == 1, deposit else if transactionType == 2 withdraw
     */
    public Transaction(Account account, TransactionType transactionType,
            double amount) {
        this.transactionType = transactionType;
        this.account = account;
        this.amount = amount;
    }

    public void run() {
        switch (this.transactionType) {
        case DEPOSIT:
            deposit();
            printBalance();
            break;
        case WITHDRAW:
            withdraw();
            printBalance();
            break;
        default:
            System.out.println("NOT A VALID TRANSACTION");
        }
        ;
    }

    public void deposit() {
        this.account.deposit(this.amount);
    }

    public void withdraw() {
        this.account.withdraw(amount);
    }

    public void printBalance() {
        System.out.println(Thread.currentThread().getName()
                + " : TransactionType: " + this.transactionType + ", Amount: "
                + this.amount);
        System.out.println("Account Balance: "
                + this.account.getAccountBalance());
    }
}

class Account {
    private int accountNumber;
    private double accountBalance;

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getAccountBalance() {
        return accountBalance;
    }

    public Account(int accountNumber) {
        this.accountNumber = accountNumber;
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean deposit(double amount) {
        if (amount < 0) {
            return false;
        } else {
            accountBalance = accountBalance + amount;
            return true;
        }
    }

    // If this method is not synchronized, you will see race condition on
    // Remove syncronized keyword to see race condition
    public synchronized boolean withdraw(double amount) {
        if (amount > accountBalance) {
            return false;
        } else {
            accountBalance = accountBalance - amount;
            return true;
        }
    }
}
九公里浅绿 2024-07-11 02:59:37

您并不总是想放弃竞争条件。 如果您有一个可由多个线程读取和写入的标志,并且该标志由一个线程设置为“完成”,以便其他线程在标志设置为“完成”时停止处理,那么您不希望出现“race”条件”予以消除。 事实上,这可以称为良性竞争条件。

然而,使用检测竞争条件的工具,它会被识别为有害的竞争条件。

有关竞争条件的更多详细信息,请参见 http://msdn.microsoft.com/en-我们/杂志/cc546569.aspx

You don't always want to discard a race condition. If you have a flag which can be read and written by multiple threads, and this flag is set to 'done' by one thread so that other thread stop processing when flag is set to 'done', you don't want that "race condition" to be eliminated. In fact, this one can be referred to as a benign race condition.

However, using a tool for detection of race condition, it will be spotted as a harmful race condition.

More details on race condition here, http://msdn.microsoft.com/en-us/magazine/cc546569.aspx.

诗笺 2024-07-11 02:59:37

考虑一个操作,一旦计数增加就必须显示计数。 即,一旦 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 显示值之前更新该值。 这里存在一个竞争条件。 竞争条件可以通过使用同步来解决

Consider an operation which has to display the count as soon as the count gets incremented. ie., as soon as CounterThread increments the value DisplayThread needs to display the recently updated value.

int i = 0;

Output

CounterThread -> i = 1  
DisplayThread -> i = 1  
CounterThread -> i = 2  
CounterThread -> i = 3  
CounterThread -> i = 4  
DisplayThread -> i = 4

Here CounterThread gets the lock frequently and updates the value before DisplayThread displays it. Here exists a Race condition. Race Condition can be solved by using Synchronzation

时光无声 2024-07-11 02:59:37

竞争条件是当两个或多个进程可以同时访问和更改共享数据时发生的一种不良情况。它的发生是因为对资源的访问存在冲突。 临界区问题可能会导致竞争条件。 为了解决进程之间的临界情况,我们一次只取出一个执行临界区的进程。

A race condition is an undesirable situation that occurs when two or more process can access and change the shared data at the same time.It occurred because there were conflicting accesses to a resource . Critical section problem may cause race condition. To solve critical condition among the process we have take out only one process at a time which execute the critical section.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文