原子操作跨进程的工作方式与跨线程的工作方式相同吗?

发布于 2024-08-08 01:48:53 字数 156 浏览 1 评论 0原文

显然,原子操作可确保不同的线程不会破坏值。但是,当使用共享内存时,跨进程仍然如此吗?即使进程碰巧被操作系统安排在不同的内核上运行?或者跨不同的不同CPU?

编辑:此外,如果它不安全,那么即使在像 Linux 这样的操作系统上(从调度程序的角度来看进程和线程是相同的)也是不安全的吗?

Obviously, atomic operations make sure that different threads don't clobber a value. But is this still true across processes, when using shared memory? Even if the processes happen to be scheduled by the OS to run on different cores? Or across different distinct CPUs?

Edit: Also, if it's not safe, is it not safe even on an operating system like Linux, where processes and threads are the same from the scheduler's point of view?

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

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

发布评论

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

评论(1

半衬遮猫 2024-08-15 01:48:53

tl;dr: 阅读原子操作文档中的细则。有些在设计上是原子的,但可能会绊倒某些变量类型。不过,一般来说,原子操作将在不同进程之间维护其契约,就像在线程之间一样。

原子操作实际上只能确保两个实体同时调用时不会出现不一致的状态。例如,由两个不同的线程或进程对同一整数调用的原子增量将始终表现如下:

  1. x = 初始值(为了便于讨论,为零)
  2. 实体 A 递增 x 并将结果返回给自身: result = x = 1。
  3. 实体 B 递增 x 并将结果返回给自身:结果 = x = 2。

其中 A 和 B 表示进行调用的第一个和第二个线程或进程。

由于竞争条件、对地址空间的不完整写入等,非原子操作可能会导致不一致或通常疯狂的结果。例如,您可以轻松地看到:

  1. x = 初始值 = 再次为零。
  2. 实体 A 调用 x = x + 1。为了计算 x + 1,A 检查 x 的值(零)并加 1。
  3. 实体 B 调用 x = x + 1。为了计算 x + 1,B 检查 x (仍然为零)并加 1。
  4. 实体 B(幸运的是)首先完成并将 x + 1 = 1(步骤 3)的结果分配给 x。 x 现在是 1。
  5. 实体 A 第二个完成并将 x + 1 = 1(步骤 2)的结果分配给 x。 x 现在是 1。

请注意实体 B 超越 A 并首先完成表达式时的竞争条件。

现在想象一下,如果 x 是一个 64 位双精度数,并且不能确保具有原子赋值。在这种情况下,您可以很容易地看到类似这样的内容:

  1. 64 位双精度 x = 0。
  2. 实体 A 尝试将 0x1122334455667788 分配给 x。首先分配前 32 位,使 x 为 0x1122334400000000。
  3. 实体 B 竞争并将 0xffeeddccbbaa9988 分配给 x。碰巧,两个 32 位一半都被更新,x 现在 = 0xffeeddccbbaa9988。
  4. 实体 A 在后半部分完成其分配,x 现在 = 0xffeeddcc55667788。

这些非原子分配是您必须诊断的最可怕的并发错误之一。

tl;dr: Read the fine print in the documentation of the atomic operations. Some will be atomic by design but may trip over certain variable types. In general, though, an atomic operation will maintain its contract between different processes just as it does between threads.

An atomic operation really only ensures that you won't have an inconsistent state if called by two entities simultaneously. For example, an atomic increment that is called by two different threads or processes on the same integer will always behave like so:

  1. x = initial value (zero for the sake of this discussion)
  2. Entity A increments x and returns the result to itself: result = x = 1.
  3. Entity B increments x and returns the result to itself: result = x = 2.

where A and B indicate the first and second thread or process that makes the call.

A non-atomic operation can result in inconsistent or generally crazy results due to race conditions, incomplete writes to the address space, etc. For example, you can easily see this:

  1. x = initial value = zero again.
  2. Entity A calls x = x + 1. To evaluate x + 1, A checks the value of x (zero) and adds 1.
  3. Entity B calls x = x + 1. To evaluate x + 1, B checks the value of x (still zero) and adds 1.
  4. Entity B (by luck) finishes first and assigns the result of x + 1 = 1 (step 3) to x. x is now 1.
  5. Entity A finishes second and assigns the result of x + 1 = 1 (step 2) to x. x is now 1.

Note the race condition as entity B races past A and completes the expression first.

Now imagine if x were a 64-bit double that is not ensured to have atomic assignments. In that case you could easily see something like this:

  1. A 64 bit double x = 0.
  2. Entity A tries to assign 0x1122334455667788 to x. The first 32 bits are assigned first, leaving x with 0x1122334400000000.
  3. Entity B races in and assigns 0xffeeddccbbaa9988 to x. By chance, both 32 bit halves are updated and x is now = 0xffeeddccbbaa9988.
  4. Entity A completes its assignment with the second half and x is now = 0xffeeddcc55667788.

These non-atomic assignments are some of the most hideous concurrent bugs you'll ever have to diagnose.

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