如果我不使用互斥锁,以下源代码有什么问题吗?
bool bStop = false;
void thread1_fun()
{
while (!bStop)
{
doSomething();
}
}
void thread2_fun()
{
bStop = true;
}
写入一个线程中的对象而另一个线程完全访问该对象是未定义的行为。
除非你特别通知编译器应该有一个围栏,例如使用std::atomic
,std::mutex
等,所有的赌注都是关闭的。
编译器有权重新编写代码,如下所示:
bool bStop = false;
void thread1_fun()
{
const bool stopped = bStop;
// compiler reasons: "because there is no fence, the variable clearly cannot
// have changed to true, since no-other thread will modify it, since
// to modify it without a fence would be UB."
while (!stopped)
{
doSomething();
}
}
void thread2_fun()
{
// this happens in my thread's view of the world,
// but no other thread need see it.
bStop = true;
}
如果我不使用互斥锁,以下源代码有什么问题吗?
是的,你有一个竞争条件,导致未定义的行为。
您需要使用std::atomic<bool>
或使用同步原语(如bStop
)保护对std::mutex
的写入/读取。
与(遗憾的)普遍看法相反,将bStop
标记为volatile
并不能解决任何问题。 volatile
限定符与线程安全无关。
在一个线程中写入变量并在另一个线程中读取该变量的值是数据竞争。具有数据竞争的程序具有未定义的行为;这意味着C ++标准没有告诉你该程序将做什么。您可以通过使变量原子或使用各种同步来避免数据竞争,最常见的是std::mutex
和std::condition_variable
。
通过分析您正在编译的代码的详细信息,编译器如何从该源代码生成目标代码的详细信息以及硬件如何处理的详细信息,您可以在不同步的情况下可靠地“工作”程序生成的对象代码。然后,如果你改变这三个中的任何一个,你必须重新进行分析,因为这些改变可能不再“起作用”了。当然,如果你这样做,那么你就会被高级语言所拯救的所有细节所困扰。除非在极少数情况下,这根本不值得努力。正确使用该语言。