【问题标题】: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、3is_connected(obj)

线程 2:连接启动时connectedHandler()

线程 3 disconnectedHandler() 连接断开时。

我认为如果重复调用connectedHandler()disconnectedHandler() 可能会出现问题,两个线程写入_connected 时出现问题并且写入出现故障,从而导致最终值错误。轮询_connected 也可能存在问题。

我的问题是:

  1. 单独的线程轮询和修改_connected 的值实际上会产生哪些潜在问题?
  2. 有哪些选项可以防止这些情况发生?也许将_connected 设置为volatile bool 可能会解决轮询值的问题。我还在考虑线程 2 和 3 修改其值的问题,也许将其设为原子 bool 并使用原子集操作将足以防止诸如乱序内存操作之类的问题。我也知道其他潜在的解决方案是锁或内存屏障,如 smb_mb。但是,我不确定应该使用什么。

非常感谢。

【问题讨论】:

  • 使其成为原子 bool 并使用原子集操作将足以防止诸如乱序内存操作之类的问题 是的,这样做。 volatile 不是线程同步技术。
  • 使用 std::atomic,或者如果您需要做的不仅仅是设置一个布尔值,请使用 std::unique_lock 和 std::mutex。但你是对的,你必须做点什么
  • 仅供参考,没有 C/C++ 代码之类的东西。您的代码使用 class 关键字,因此它是 C++ 而不是 C 语言。我强烈建议不要混合这两种语言,这会使您的程序更复杂,增加更多缺陷并且更难维护。

标签: c++ multithreading multiprocessing


【解决方案1】:

单独的线程轮询和修改 _connected 的值实际上会产生哪些潜在问题?

无论如何,这是未定义的行为。

有什么办法可以防止这些?

一个常见的解决方案是使用std::atomic&lt;bool&gt; 而不是bool

有一些更奇特(也更复杂)的方法来确保线程之间的同步,但std::atomic 是一个很好的首选,而且正确使用也不难。

也许将_connected 设为volatile bool 可能会解决问题

不会的。 volatile does not solve thread synchronization issues.

【讨论】:

  • 谢谢。我没有同步其他任何东西,只是避免未定义的行为并确保没有错误,所以我想将 _connected 声明为std::atomic&lt;bool&gt; _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 得到更多的关注。我认为这是一个很好的问题。
  • 谢谢,我会这样做的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-12
  • 2019-11-23
  • 1970-01-01
  • 2013-06-30
  • 2023-04-05
  • 2021-03-17
相关资源
最近更新 更多