【问题标题】:C++ Thread does not detect global variable changeC++ 线程未检测到全局变量更改
【发布时间】:2020-09-16 12:58:36
【问题描述】:

我有 2 个线程监视同一个全局 state,如果 state.shutdown 变为 false,则线程 run() 应该返回。代码如下。

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

using namespace std;

struct State {
    bool shutdown = false;

    ~State() {
        shutdown = true;
    }
};

State state;

#define CHECK_SHUTDOWN     \
  {                        \
    std::cout << (state.shutdown ? " SHUTDOWN " : " NOSHUT ") << typeid(*this).name() << std::endl; \
    if (state.shutdown) {  \
        return;            \
    }                      \
  }

class Mythread {
public:
    void join();
    void run();
    void launch();
    std::thread self_thread;
};

void Mythread::run() {
    while(1) {
        CHECK_SHUTDOWN
    }
}

void Mythread::join() {
    if (self_thread.joinable()) {
        self_thread.join();
    }
}

void Mythread::launch() {
    self_thread = std::thread(&Mythread::run, this);
}

std::mutex mtx;
void shut() {
    std::lock_guard<std::mutex> lock(mtx);   
    state.shutdown = true;
}

int main()
{
    Mythread thread1;
    Mythread thread2;

    thread1.launch();
    thread2.launch();

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));


    //state.shutdown = true;
    shut(); //This makes no difference with the line above

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    thread1.join();
    thread2.join();


    return 0;
}

但是,即使我手动将 state.shutdown 设置为 true,线程也永远无法检测到它。我得到了这样的打印:

 NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                                                                                                                                

...Program finished with exit code 0                                                                             
Press ENTER to exit console. 

最后。我也很困惑,因为永远不会返回 run() 函数,线程连接应该挂起。但是线程可以成功加入。

任何帮助将不胜感激!

【问题讨论】:

  • 欢迎来到“sequencing”这个精彩的话题,这是一组规则,指定线程如何“看到”来自其他线程的对象的变化。这需要 两个 线程、更改对象的线程和“看到”对象的线程采取特定的、显式的操作。规则很复杂,无法在 stackoverflow.com 上的一两段中完全概括,除非过于简化并遗漏了一堆细节。如果您真的想正确学习该主题,请参阅您的 C++ 教科书对该主题的冗长讨论。
  • 简单的解决方法是将State::shutdown 设置为std::atomic&lt;bool&gt;,但听从Sam 的建议并阅读为什么这是必要的,你会得到很好的服务。
  • @SamVarshavchik 感谢您的回复。你能指点我任何在线资源吗?我在我的 C++ 入门书(第 4 版)中找不到它。

标签: c++ multithreading global-variables mutex


【解决方案1】:

您在关机时有一个data race

当一个表达式的计算写入一个内存位置而另一个计算读取或修改相同的内存位置时,表达式被称为冲突。具有两个冲突评估的程序存在数据竞争 [...]

shut() 中,您使用互斥锁设置了shutdown 标志,但是在没有互斥锁的情况下执行了检查(并且State 析构函数也不使用互斥锁)。因此,您对非原子变量有冲突的操作(读+写),而没有适当的发生在关系之前。这是导致未定义行为的数据竞争。

简单的解决方案是将shutdown 设为std::atomic&lt;bool&gt;,然后您甚至不需要互斥锁来设置标志。

有关数据竞争和 C++ 内存模型的更多详细信息,我可以推荐我与他人合着的这篇论文:Memory Models for C/C++ Programmers

【讨论】:

    猜你喜欢
    • 2020-12-15
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多