7

如果我了解 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;
        }
}
4

3 回答 3

16

我对么?

不会。这将制作一份副本以返回;但是在不同步的情况下读取它以制作该副本会产生数据竞争,具有未定义的行为。您需要在互斥锁锁定的情况下制作它的本地副本:

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>从多线程访问简单类型更方便,也可能更高效。

于 2013-05-13T13:12:53.153 回答
3

竞争条件与变量是位于堆上还是栈上无关。竞争条件是当一个线程正在修改一个变量(内存位置)而另一个线程正在读取或修改同一个变量时。不能保证 a 的修改bool是原子的,因此发布的代码具有竞争条件,因此具有未定义的行为。

一个修复方法是存储bool互斥锁被持有时的值并返回变量:

static bool IsShutdownInProgress()
{
    pthread_mutex_lock(&mutex);
    bool result = shutdownInProgress;
    pthread_mutex_unlock(&mutex);
    return result;
}

引入了 c++11std::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;
}

如果 c++11 不可用,boost::atomic则在 v1.53.0 中引入,并且 boost 也有等效的boost::mutexboost::lock_guard.

于 2013-05-13T13:13:18.250 回答
1

是的,它需要一个锁

C++11 的内存模型指出,如果任何线程在写入值的同时另一个线程正在读取它,那么就会出现数据竞争。这是因为读取和/或写入都可能不是原子的。

在这种情况下,您将从函数返回一个局部变量,但要获得该局部变量,编译器需要复制 中的值shutdownInProgress,该值可能同时被另一个调用的线程更改ShutdownIsInProgress()

解决这个问题的一个简单方法是创建shutdownInProgress一个原子:

static std::atomic<bool> shutdownInProgress;

如果你把它变成原子的,那么任何一个功能都不需要任何锁

于 2013-05-13T13:12:43.990 回答