记一次多线程排障

问题:一个简单的SPSC模型,消费者线程执行一次循环就退出。
问题代码大致是这样:
class DumperThread
{
protected:
DumpQueue queue_;
std::thread thread_;
alignas(64) std::atomic_bool running {false};
public:
DumperThread(): queue_(), thread_(&DumperThread::run, this) {}
void run()
{
running.store(true);
while (running.load())
{
auto fut = std::move(queue_.pop());
fut.get();
}
}
}
分析
调试初期发现 running 在 queue_.pop() 时变为 false,百思不得其解。一开始考虑可能是 queue 里面哪里写越界了,或者 DumperThread 对象本身析构了。
多次调试偶然注意到 running 变为 false 的时机不确定,开始考虑可能是数据竞争。再仔细分析代码,发现 thread_(&DumperThread::run, this) 在初始化时构造,构造后线程已经可能开始运行。由于C++的初始化顺序是按照声明顺序,因此 running 在 thread_ 之后初始化,可能在 run 内部的赋值之后初始化,从而造成线程状态错误,循环退出。
修改
修改也很简单,将两者声明顺序调换,确保初始化 running 之后线程才可能开始运行:
protected:
DumpQueue queue_;
alignas(64) std::atomic_bool running {false};
std::thread thread_;
