原子指令和变量更新可见性

发布于 2024-08-08 13:21:12 字数 782 浏览 4 评论 0 原文

在大多数常见平台上(最重要的是 x86;我知道某些平台具有极其困难的内存模型,几乎不提供对多线程有用的保证,但我不关心罕见的反例),以下代码安全吗?

线程 1:

someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);

线程 2:

while(!atomicRead(stuffDoneFlag)) {}  // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);

假设原子操作的标准、合理实现:

  1. 线程 1 对 someVariable 的赋值是否能保证在调用 atomicSet() 之前完成?
  2. 如果线程 2 以原子方式读取 stuffDoneFlag,那么在调用 doMoreStuff() 之前,线程 2 是否能保证看到对 someVariable 的赋值?

编辑:

  1. 我正在使用的原子操作的实现包含每个中的 x86 LOCK 指令 操作,如果有帮助的话。
  2. 假设 stuffDoneFlag 已以某种方式正确清除。怎么做并不重要。
  3. 这是一个非常简单的例子。我以这种方式创建它,这样您就不必了解问题的整个背景来回答它。我知道这效率不高。

On most common platforms (the most important being x86; I understand that some platforms have extremely difficult memory models that provide almost no guarantees useful for multithreading, but I don't care about rare counter-examples), is the following code safe?

Thread 1:

someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);

Thread 2:

while(!atomicRead(stuffDoneFlag)) {}  // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);

Assuming standard, reasonable implementations of atomic ops:

  1. Is Thread 1's assignment to someVariable guaranteed to complete before atomicSet() is called?
  2. Is Thread 2 guaranteed to see the assignment to someVariable before calling doMoreStuff() provided it reads stuffDoneFlag atomically?

Edits:

  1. The implementation of atomic ops I'm using contains the x86 LOCK instruction in each
    operation, if that helps.
  2. Assume stuffDoneFlag is properly cleared somehow. How isn't important.
  3. This is a very simplified example. I created it this way so that you wouldn't have to understand the whole context of the problem to answer it. I know it's not efficient.

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

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

发布评论

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

评论(5

话少情深 2024-08-15 13:21:12

如果您的实际 x86 代码在线程 1 中的atomicSet 中存储之前存储了 someVariable,并在线程 2 中的atomicRead 中加载之后加载了someVariable,那么您应该没问题。 英特尔软件开发人员手册第 3A 卷在第 8.2 节中指定了 x86 的内存模型,并且线程内存储-存储和加载-加载约束在这里应该足够了。

但是,可能没有任何东西可以阻止编译器对原子操作中使用的任何高级语言生成的指令进行重新排序。

If your actual x86 code has the store to someVariable before the store in atomicSet in Thread 1 and load of someVariable after the load in atomicRead in Thread 2, then you should be fine. Intel's Software Developer's Manual Volume 3A specifies the memory model for x86 in Section 8.2, and the intra-thread store-store and load-load constraints should be enough here.

However, there may not be anything preventing your compiler from reordering the instructions generated from whatever higher-level language you are using across the atomic operations.

挽心 2024-08-15 13:21:12

1) 是

2) 是

两者都有效。

1)Yes

2)Yes

Both work.

独留℉清风醉 2024-08-15 13:21:12

这段代码看起来线程安全,但我质疑 spinlock ( while 循环)的效率,除非你只旋转了很短的时间。在任何给定系统上都不能保证线程 2 不会完全占用所有处理时间。

我建议使用一些实际的同步原语(看起来像 boost::condition_variable 就是你想要的)而不是依赖自旋锁。

This code looks thread safe, but I question the efficiency of your spinlock (the while loop) unless you are only spinning for a very short amount of time. There is no guarantee on any given system that Thread 2 won't completely hog all processing time.

I would recommend using some actual synchronization primitives (looks like boost::condition_variable is what you want here) instead of relying on the spin lock.

堇色安年 2024-08-15 13:21:12

原子指令确保线程 2 在线程 2 继续之前等待线程 1 完成变量设置。但是,有两个关键问题:

1) someVariable 必须声明为“易失性',以确保编译器不会优化其分配,例如将其存储在寄存器中或推迟写入。

2)第二个线程在等待信号时阻塞(称为自旋锁定)。您的平台可能提供了更好的锁定和信号原语和机制,但相对简单的改进是在线程 2 的 while() 主体中简单地使用 sleep()

The atomic instructions ensure that the thread 2 waits for thread 1 to complete setting the variable before thread 2 proceeds. There are, however, two key issues:

1) the someVariable must be declared 'volatile' to ensure that the compiler does not optimise it's allocation e.g. storing it in a register or deferring a write.

2) the second thread is blocking while waiting for the signal (termed spinlocking). Your platform probably provides much better locking and signalling primatives and mechanisms, but a relatively straightforward improvement would be to simply sleep() in the thread 2's while() body.

静待花开 2024-08-15 13:21:12

dsimcha 写道: “假设 stuffDoneFlag 以某种方式被正确清除。如何并不重要。”
这不是真的!

让我们看看场景:

  1. Thread2 检查 stuffDoneFlag 是否为 1 开始读取 someVariable。
  2. 在 Thread2 完成读取之前,任务调度程序会中断其任务并将任务挂起一段时间。
  3. Thread1再次访问someVariable并更改内存内容。
  4. 任务调度程序再次打开Thread2,它继续工作,但某些变量的内存内容发生了变化!

dsimcha written: "Assume stuffDoneFlag is properly cleared somehow. How isn't important."
This is not true!

Let's see scenario:

  1. Thread2 checks the stuffDoneFlag if it's 1 start reading someVariable.
  2. Before the Thread2 finish reading the task scheduler interrupt its task and suspend the task for some time.
  3. Thread1 again access to the someVariable and change the memory content.
  4. Task scheduler switch on again Thread2 and it continue the job but memory content of someVariable is changed!
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文