跨多个线程和 cpu 安全地修改和读取布尔值的选项都有哪些?
Posted
技术标签:
【中文标题】跨多个线程和 cpu 安全地修改和读取布尔值的选项都有哪些?【英文标题】:What are the options for safely modifying and reading a boolean across multiple threads and cpus?跨多个线程和 cpu 安全地修改和读取布尔值的选项有哪些? 【发布时间】:2021-11-10 19:57:24 【问题描述】:我有一些 C++ 代码大致包含这个逻辑:
class wrapper_info
public:
bool isConnected();
void connectedHandler();
void disconnectedHandler();
protected:
bool _connected;
void wrapper_info::connectedHandler()
_connected = true;
void wrapper_info::disconnectedHandler()
_connected = false;
bool wrapper_info::isConnected()
return _connected;
extern "C"
bool is_connected(void *obj)
wrapper_info *wrapper_obj = reinterpret_cast<wrapper_info*>(obj);
return wrapper_obj->isConnected();
由于我无法控制的原因,不同的线程(在不同的 CPU 内核上运行)以下列方式调用这些函数。
线程 1、2、3:is_connected(obj)
线程 2:连接启动时connectedHandler()
。
线程 3 disconnectedHandler()
连接断开时。
我认为如果重复调用connectedHandler()
和disconnectedHandler()
可能会出现问题,两个线程写入_connected
时出现问题并且写入出现故障,从而导致最终值错误。轮询_connected
也可能存在问题。
我的问题是:
-
单独的线程轮询和修改
_connected
的值实际上会产生哪些潜在问题?
有哪些选项可以防止这些情况发生?也许将_connected
设置为volatile bool
可能会解决轮询值的问题。我还在考虑线程 2 和 3 修改其值的问题,也许将其设为原子 bool 并使用原子集操作将足以防止诸如乱序内存操作之类的问题。我也知道其他潜在的解决方案是锁或内存屏障,如 smb_mb。但是,我不确定应该使用什么。
非常感谢。
【问题讨论】:
使其成为原子 bool 并使用原子集操作将足以防止诸如乱序内存操作之类的问题 是的,这样做。volatile
不是线程同步技术。
使用 std::atomic,或者如果您需要做的不仅仅是设置一个布尔值,请使用 std::unique_lock 和 std::mutex。但你是对的,你必须做点什么
Is it ok to read a shared boolean flag without locking it when another thread may set it (at most once)?
仅供参考,没有 C/C++ 代码之类的东西。您的代码使用 class
关键字,因此它是 C++ 而不是 C 语言。我强烈建议不要混合这两种语言,这会使您的程序更复杂,增加更多缺陷并且更难维护。
【参考方案1】:
单独的线程轮询和修改 _connected 的值实际上会产生哪些潜在问题?
无论如何,这是未定义的行为。
有什么办法可以防止这些?
一个常见的解决方案是使用std::atomic<bool>
而不是bool
。
有一些更奇特(也更复杂)的方法来确保线程之间的同步,但std::atomic
是一个很好的首选,而且正确使用也不难。
也许将
_connected
设为volatile bool
可能会解决问题
不会的。 volatile
does not solve thread synchronization issues.
【讨论】:
谢谢。我没有同步其他任何东西,只是避免未定义的行为并确保没有错误,所以我想将 _connected 声明为std::atomic<bool> _connected
就足以使用_connected.store(true/false, std::memory_order_relaxed)
和_connected.load(std::memory_order_relaxed)
?我想我不需要其他内存订单,因为我不关心线程在修改 _connected 之前看到其他事情已经发生(没有其他事情发生)。这是正确的逻辑吗?如果有帮助,这将在 linux x86_64 上运行
为了详细说明以防我没有意义,我的理解是在线程1中的int val = 0; _connected.store(true, memory_order_release)
和线程2中的_connected.load(memory_order_acquire); int x = val;
这样的情况下使用其他内存顺序,例如memory_order_release和memory_order_acquire(确保 x 为 0)。但是,我没有任何其他依赖项,所以我认为我可以使用轻松。
@someoneserious 如果您有新问题,我推荐使用 [] 按钮。你的问题会比这里的 cmets 得到更多的关注。我认为这是一个很好的问题。
谢谢,我会这样做的以上是关于跨多个线程和 cpu 安全地修改和读取布尔值的选项都有哪些?的主要内容,如果未能解决你的问题,请参考以下文章