int变量的并发递增

Concurrent incrementing of a int variable

本文关键字:并发 变量 int      更新时间:2023-10-16

面试中的一个问题

int count = 0;
void func1()
{
  for ( int i =0 ; i < 10; ++i )
    count = count + 1;
}
void func2()
{
  for ( int i =0 ; i < 10; ++i )
    count++;
}
void func3()
{
  for ( int i =0 ; i < 10; ++i )
    ++count;
}
int main()
{
  thread(func1);
  thread(func2);
  thread(func3);
  //joining all the threads
  return 0;
}

问题是:理论上count的取值范围是多少?上界显然是30,下界是多少?他们告诉我这是10,但我不确定。否则,我们为什么需要内存屏障?

范围的下界是多少?

这是未定义的行为,所以count可以取任何值可以想象。否则程序会崩溃

James Kanze的答案对于所有实际目的来说都是正确的,但是在这种特殊情况下,如果代码完全按照所写的并且这里使用的thread是来自c++ 11的std::thread,则行为实际上是定义的。

特别地,thread(func1);将启动一个运行func1的线程。然后,在表达式结束时,临时线程对象将被销毁,没有对其调用join或detach。因此线程仍然是可接合的,标准定义在这种情况下,析构函数调用std::terminate。(参见[thread.thread.destr]: "If joinable() then terminate(),否则没有效果。")所以你的程序终止了。

因为这发生在第二个线程启动之前,所以没有实际的竞争条件——第一个线程是唯一一个接触count的线程,如果它甚至达到了那个程度。

从简单的部分开始,明显的上限是30,因为如果一切顺利,你有3个函数调用;每个都可以将count增加10倍。总体:3 * 10 = 30。

对于下限,它们是正确的,这就是为什么-最坏的情况是每次一个线程试图增加count时,其他线程将在完全相同的时间这样做。请记住,++count实际上是以下伪代码:

count_temp = count;
count_temp = count_temp+1;
count = count_temp;

很明显,如果它们同时执行相同的代码,则只有10个实际增量,因为它们都读取相同的count初始值,并且都写回相同的增加值。

首先,我要感谢你们给了我深入阅读标准的理由。否则我无法继续这场辩论。

标准在第1.10节第21条:The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

中表述得非常清楚

然而,undefined behavior这个术语也在标准1.3.24节:behavior for which this International Standard imposes no requirements... Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment...

中有定义

考虑到Sebasian关于std::terminate的回答,并假设这些线程不会抛出异常从而导致过早终止;虽然标准没有定义结果,但由于算法的简单性,结果可能是相当明显的。换句话说,虽然100%准确的答案是结果是不确定的,但我仍然认为可能结果的范围是明确的,由于characteristic of the environment,它是10-30。

BTW -我真的想做一个评论,而不是另一个答案,但是它太长了