Coding

记一次多线程排障

排查因初始化顺序导致的多线程数据竞争

JamzumSum · ·updated 2026年6月21日 · 1 min read
Photo by deleted.

问题:一个简单的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();
		}
	}
}

分析

调试初期发现 runningqueue_.pop() 时变为 false,百思不得其解。一开始考虑可能是 queue 里面哪里写越界了,或者 DumperThread 对象本身析构了。

多次调试偶然注意到 running 变为 false 的时机不确定,开始考虑可能是数据竞争。再仔细分析代码,发现 thread_(&DumperThread::run, this) 在初始化时构造,构造后线程已经可能开始运行。由于C++的初始化顺序是按照声明顺序,因此 runningthread_ 之后初始化,可能在 run 内部的赋值之后初始化,从而造成线程状态错误,循环退出。

修改

修改也很简单,将两者声明顺序调换,确保初始化 running 之后线程才可能开始运行:

protected:
	DumpQueue queue_;
	alignas(64) std::atomic_bool running {false};
	std::thread thread_;
分享
作者
JamzumSum

Open-source developer, major language: Python, C++.

Comments

Hook this up to your favourite commenting platform — Giscus, Disqus, or your own.

Continue reading