在大多数常见平台上(最重要的是 x86;我知道某些平台具有极其困难的内存模型,几乎不提供对多线程有用的保证,但我不关心罕见的反例),以下代码安全吗?
线程 1:
someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);
线程 2:
while(!atomicRead(stuffDoneFlag)) {} // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);
假设原子操作的标准、合理实现:
- 线程 1 对
someVariable
的赋值是否能保证在调用 atomicSet()
之前完成?
- 如果线程 2 以原子方式读取
stuffDoneFlag
,那么在调用 doMoreStuff()
之前,线程 2 是否能保证看到对 someVariable
的赋值?
编辑:
- 我正在使用的原子操作的实现包含每个中的 x86
LOCK
指令
操作,如果有帮助的话。
- 假设
stuffDoneFlag
已以某种方式正确清除。怎么做并不重要。
- 这是一个非常简单的例子。我以这种方式创建它,这样您就不必了解问题的整个背景来回答它。我知道这效率不高。
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:
- Is Thread 1's assignment to
someVariable
guaranteed to complete before atomicSet()
is called?
- Is Thread 2 guaranteed to see the assignment to
someVariable
before calling doMoreStuff()
provided it reads stuffDoneFlag
atomically?
Edits:
- The implementation of atomic ops I'm using contains the x86
LOCK
instruction in each
operation, if that helps.
- Assume
stuffDoneFlag
is properly cleared somehow. How isn't important.
- 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.
发布评论
评论(5)
如果您的实际 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.
1) 是
2) 是
两者都有效。
1)Yes
2)Yes
Both work.
这段代码看起来线程安全,但我质疑 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.
原子指令确保线程 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'swhile()
body.dsimcha 写道: “假设 stuffDoneFlag 以某种方式被正确清除。如何并不重要。”
这不是真的!
让我们看看场景:
dsimcha written: "Assume stuffDoneFlag is properly cleared somehow. How isn't important."
This is not true!
Let's see scenario: