多线程访问全局变量:我应该使用互斥锁吗

Posted

技术标签:

【中文标题】多线程访问全局变量:我应该使用互斥锁吗【英文标题】:Multithreaded access to global variable : should I use mutex 【发布时间】:2019-12-19 10:08:33 【问题描述】:

假设我有 2 个线程:std::thread thd1; std::thread thd2; 线程 thd1 定期设置一些全局变量,调用以下 setFlag 函数:

static std::int32_t g_flag;
static std::mutex io_mutex;

void setFlag( std::int32_t flag )

   //std::lock_guard<std::mutex> lk(io_mutex);
   g_flag = flag;

线程 thd2 会定期读取这个标志

std::int32_t getFlag()

   //std::lock_guard<std::mutex> lk(io_mutex);
   return g_flag;

问题是——在这种情况下我应该使用互斥锁吗?在没有互斥锁的情况下从多个线程以读写方式访问变量是否安全?

【问题讨论】:

在这种情况下,原子可能也是合适的。 仅供参考:SO: Multithreading program stuck in optimized mode but runs normally in -O0 仅供参考:SO: While loop in main thread is getting stuck when using std::thread 【参考方案1】:

访问内存位置以在一个线程中进行写入以及在另一个线程中进行读取或写入而没有同步且至少其中一个是非原子的,这被称为数据竞争并导致未定义C++ 中的行为。

在您的代码中,线程 1 对 g_flag 的写访问与线程 2 对同一变量的读访问不同步。

因此,您的程序具有未定义的行为(因为所有访问都不是原子的)。

一种可能的解决方案是使用互斥锁,正如您在注释代码中正确演示的那样,这将同步读取和写入访问,这样一个先发生另一个,尽管这些发生之前的顺序仍然不确定。

另一种可能性是将g_flag 声明为原子:

std::atomic<std::int32_t> g_flag;

如上所述,原子访问(std::atomic 提供)在可能并行访问以进行写入和读取时特别避免导致数据争用和未定义行为。

一个原子(通常)不会像互斥锁/锁那样让其他线程等待。但是,如果您还要访问其他共享内存,这也会使正确使用变得更加棘手。

相反,std::atomic 有更多选项来指定是否以及如何对原子访问周围的其他内存访问进行排序,即它是否以及在何种程度上也会导致线程之间的同步。

如果没有更多详细信息,我无法确定适合您的工具是什么。

【讨论】:

以上是关于多线程访问全局变量:我应该使用互斥锁吗的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal和异步

使用具有共享全局变量的线程

交替打印ABC多线程(互斥量+条件变量)

交替打印ABC多线程(互斥量+条件变量)

Python 多进程和多线程 的使用

多线程通信