增加 c++ 中的变量值时的竞争条件

发布于 2025-01-18 14:42:16 字数 372 浏览 1 评论 0原文

最近在一次采访中问了这个问题。问题是,我们是否有一个函数,将int i作为参数的引用,并且只有i ++。没有任何线程同步。现在在main函数中,我们以0初始化变量i,然后创建7个新线程以运行相同的函数并传递相同的变量i ,毕竟所有线程被连接在一起,变量i具有多少个可能的值?非常感谢!

我尝试考虑执行i ++的说明。喜欢加载add存储指令。假设我们正在使用g ++编译器和Linux OS。

I was asked this question in an interview recently. The question is if we have a function that takes a reference of int i as parameter and only does i++. There is no any thread synchronizations. Now in main function, we initialize variable i with 0, then we create 7 new threads to run the same function and pass the same variable i, and after all threads are joined, how many possible values the variable i has? Thank you very much!

I tried thinking about the instructions of doing i++. Like load, add and store instructions. Assume we’re using g++ compiler and in Linux OS.

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

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

发布评论

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

评论(3

想念有你 2025-01-25 14:42:16

用于递增变量的具体指令取决于指令集,但除非使用某种原子增量,否则它将分解为您所描述的加载/添加/存储操作。在 x86 上,这可以使用单个 inc 指令来完成,但如果没有锁定,它仍然会分解为内部加载/添加/存储操作。

您将需要查看这些序列可能的交错。交错是由非原子序列的中断引起的。以下是一种可能的交错:

thread 1      thread 2
 load   
               load
               add
               store
 add
 store

该序列将使变量仅由线程 1 递增一次,因为线程 2 的递增操作实际上被忽略 — 线程 1 存储在最后,因此“获胜”。 (无论如何,线程 2 在某种意义上以错误的值开始,因此线程 2 的存储与线程 1 的存储具有相同的值 (+1)。)

因此,在一个极端情况下,答案是变量将仅递增 1。  在另一个极端,如果每个线程在不中断该序列的情况下成功递增,则变量将递增 7 次。所有中间值(增加 2-6)也是可能的。


由于根本没有同步,我们还必须考虑在连接后观察到原始 0 的可能性,尽管我认为这不太可能,因为创建线程和连接它们所涉及的系统调用是自然同步的。

The specific instructions used to increment the variable depends on the instruction set, but unless using some kind of atomic increment, it will break down into load/add/store operations as you are describing.  On x86 that might be done with a single inc instruction, but without locking it will still break down to internal load/add/store operations.

You will want to look at the possible interleaving of those sequences.  The interleaving is caused by interruption of the non-atomic sequences.  Here is one possible such possible interleaving:

thread 1      thread 2
 load   
               load
               add
               store
 add
 store

That sequence will leave the variable incremented only once, by thread 1, because thread 2's increment operation is effectively ignored — thread 1 stores last so "wins".  (Thread 2 started with the wrong value in some sense anyway, so thread 2's store has the same value (+1) as thread 1's store.)

So, on one extreme the answer would be that the variable will be incremented only by one.  On the other extreme, if each thread successfully increments without interruption of that sequence, then the variable would be incremented 7 times.  All intermediate values (incremented by 2-6) are also possible.


Since there is no synchronization at all, we also have to consider the possibility that we observe the original 0 after the joins, though I think this is unlikely due to the natural synchronization of system calls involved in creating threads and joining them.

梦里泪两行 2025-01-25 14:42:16

您描述的代码具有未定义的行为。 i 的值未定义。如果您编译并运行代码并在屏幕上打印 i ,则输出可能是任何内容。它可以是 42“hello world”

也许这个问题是为了让您考虑有多少种不同的方法可以让多个线程递增 i 以及这如何影响输出。然而,当代码具有未定义的行为时,逻辑推理是徒劳的。

另一方面,如果您关心编译后的代码中实际发生的情况,那么了解这一点的唯一方法就是查看编译器生成的程序集。结果程序的作用是未定义的,但它确实做了一些事情。尽管你只能在编译它并研究程序集之后才能知道它是什么。 C++ 标准没有描述这样的程序将做什么,并且你得到的任何东西都是不可移植的。

The code you describe has undefined behavior. The value of i is undefined. If you do compile and run the code and print i on the screen the output could be anything. It could be 42 or "hello world".

Perhaps the question was meant to tease you into considering how many different ways there are to have multiple threads increment i and how that could affect the output. However, when code has undefined behavior, then logical reasoning is futile.

On the other hand, if you care about what actually happens in the compiled code, then the only way to know that is to look at the compiler generated assembly. What the resulting program does is undefined, but it certainly does something. Though you can only know what that is after you compiled it and study the assembly. The C++ standard does not describe what such a program will do and whatever you get is not portable.

权谋诡计 2025-01-25 14:42:16

如其他答案中所述,有UB。

同样,可能会有这种数字根本没有增加或增加几次(少于7)或其他任何情况的情况。由于它不是原子变量(问题没有提到) - 它将产生非常不稳定/不可预测的结果。有关更多解释,请参见/Google。有关“ 内存模型

As mentioned in other answers, there is UB.

Also, it is possible to have situations where this number isn't increased at all or incremented a few times (less than 7) or whatever else. Since it's not an atomic variable (the question didn't mention that) - it will yield very unstable/unpredictable results. See/google for "memory model" for more explainations.

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