我有一个后台线程,它在状态变量done
上循环。当我想停止线程时,我将变量done
设置为true
。但显然这个变量从未设定过。我知道编译器可能会对其进行优化,因此我标记了done
volatile
。但这似乎没有任何影响。注意,我并不担心竞争条件,所以我没有使它atomic
或使用任何同步结构。如何让线程在每次迭代时都不跳过测试变量?或者问题完全是另一回事? done
最初为false
。
struct SomeObject
{
volatile bool done_;
void DoRun();
};
static void RunLoop(void* arg)
{
if (!arg)
return;
SomeObject* thiz = static_cast<SomeObject*>(arg);
while( !(thiz->done_) ) {
thiz->DoRun();
}
return;
}
答案 0 :(得分:3)
volatile
在C ++中没有任何多线程含义。它是来自C的保留,用作信号处理程序触及的sig_atomic_t
标志的修饰符以及对内存映射设备的访问。没有语言强制要求C ++函数重新访问内存,这会导致竞争条件(读者从未打扰过两次检查&#34;优化&#34;),其他人注意到这一点。
使用std::atomic
(来自C ++ 11或更高版本)。
它可以,而且通常是无锁的:
struct SomeObject {
std::atomic_bool done_;
void DoRun();
bool IsDone() { return done_.load(); }
void KillMe() { done_.store(true); }
};
static void RunLoop(void *arg) {
SomeObject &t = static_cast<SomeObject &>(*arg);
cout << t.done_.is_lock_free(); // some archaic platforms may be false
while (!t.IsDone()) {
t.DoRun();
}
}
load()
和store()
方法强制编译器至少在每次迭代时检查内存位置。对于x86[_64]
,SomeObject
实例的done_
成员的缓存行将被缓存并在本地检查,没有锁定,甚至原子/锁定内存读取原样。如果你做的事情比单向标志集更复杂,你需要考虑使用明确的内存栅栏等等。
Pre-C ++ 11没有多线程内存模型,因此您必须依赖具有特殊编译器权限(如pthread)的第三方库,或使用特定于编译器的功能来获取等效内容。 /强>
答案 1 :(得分:1)
这与您在msvc 2010中的预期效果相同。如果我删除volatile,它将永远循环。如果我留下挥发性,它就有效。这是因为microsoft像你期望的那样对待volatile,这与iso不同。
这也有效:
struct CDone {
bool m_fDone;
};
int ThreadProc(volatile CDone *pDone) {
}
以下是MSDN的说法:
http://msdn.microsoft.com/en-us/library/12a04hfd.aspx
声明为volatile的对象不会在某些优化中使用,因为它们的值可能随时更改。系统始终在请求时读取volatile对象的当前值,即使前一条指令要求来自同一对象的值也是如此。
此外,对象的值在分配时立即写入。
符合ISO标准:
如果您熟悉C#volatile关键字,或者熟悉早期版本的Visual C ++中volatile的行为,请注意C ++ 11 ISO标准的volatile关键字是不同的,并且当/ volatile:指定iso编译器选项。 (对于ARM,默认情况下指定)。 C ++ 11 ISO标准代码中的volatile关键字仅用于硬件访问;不要将它用于线程间通信。对于线程间通信,请使用来自C ++标准模板库的std :: atomic等机制。