如果我理解C ++编译器如何处理局部变量,那么IsShutdownInProgress()
不需要任何锁定,因为shutdownInProgress
静态变量将放在堆栈上。我是对的吗?
class MyClass
{
private:
// Irrelevant code commented away
static pthread_mutex_t mutex;
static bool shutdownInProgress;
public:
static void ShutdownIsInProgress()
{
pthread_mutex_lock(mutex);
shutdownInProgress = true;
pthread_mutex_unlock(mutex);
}
static bool IsShutdownInProgress()
{
// pthread_mutex_lock(mutex);
// pthread_mutex_unlock(mutex);
return shutdownInProgress;
}
}
答案 0 :(得分:12)
我说错了吗?
没有。这将使它的副本返回;但阅读它以使该副本不同步将产生数据竞争,具有未定义的行为。您需要在锁定互斥锁的情况下制作本地副本:
static bool IsShutdownInProgress()
{
pthread_mutex_lock(mutex);
bool result = shutdownInProgress;
pthread_mutex_unlock(mutex);
return result;
}
或者,使用不太容易出错的RAII锁定类型:
static bool IsShutdownInProgress()
{
lock_guard lock(mutex);
return shutdownInProgress;
}
在C ++ 11中,您可以考虑使用std::atomic<bool>
从多个线程访问简单类型,以便更方便,也许更有效。
答案 1 :(得分:3)
竞争条件与变量是位于堆上还是堆栈上无关。竞争条件是一个线程正在修改变量(内存位置)而另一个线程正在读取或修改同一个变量。无法保证bool
的修改是原子的,因此发布的代码具有竞争条件,因此未定义的行为。
修复是在保持互斥锁时存储bool
的值并返回变量:
static bool IsShutdownInProgress()
{
pthread_mutex_lock(&mutex);
bool result = shutdownInProgress;
pthread_mutex_unlock(&mutex);
return result;
}
c ++ 11引入了可以使用的std::mutex
和std::lock_guard
,使用lock_guard
将避免要求临时变量存储bool
值回报:
static std::mutex mtx_;
static bool IsShutdownInProgress()
{
std::lock_guard<std::mutex> lk(mtx_);
return shutdownInProgress;
}
c ++ 11还引入了std::atomic<>
,它将确保修改是原子的,并且无需显式锁定:
static std::atomic<bool> shutdownInProgress;
static bool IsShutdownInProgress()
{
return shutdownInProgress;
}
如果{+ 3}}的c ++ 11不可用,则在v1.53.0中引入,而且boost也具有等效的boost::atomic
。
答案 2 :(得分:1)
是的,需要锁定
C ++ 11的内存模型声明如果有任何线程在读取它的同时写入一个值,那么你就有数据竞争。这是因为读取和/或写入都可能不是原子的。
在这种情况下,您将从函数返回一个本地,但是为了获得该本地,编译器需要复制shutdownInProgress
中的值,该值可能被另一个调用ShutdownIsInProgress()
的线程同时更改。
解决此问题的一种简单方法是使shutdownInProgress
成为原子:
static std::atomic<bool> shutdownInProgress;
如果你使它成为原子,你根本不需要任何锁定