提问人:Jixxy 提问时间:6/9/2022 最后编辑:Jixxy 更新时间:6/9/2022 访问量:88
奇怪的多线程行为 - CompilerExplorer 链接
Weird multithreading behavior - CompilerExplorer link
问:
TL的;DR:为什么这 https://godbolt.org/z/ohK31hW34 多线程程序会出现段错误?
解释:我遇到了多线程 C++ 应用程序的奇怪行为。应用程序具有多个线程,这些线程在由变量保护的 while 循环中循环。我在多个地方使用这个构造,所以我用&方法将其提取到一个简单的类中。std::atomic<bool>
ThreadLoop
Start(function)
Stop()
class ThreadLoop
{
public:
ThreadLoop(const std::string& name) : mName(name) {}
~ThreadLoop() { Stop(); }
template <typename F>
void Start(F&& function)
{
if (mRunning)
return;
std::scoped_lock lock(mMutex);
if (mThread.joinable())
mThread.join();
mRunning = true;
mThread = std::thread([&]() {
while (mRunning)
{
function();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
}
void Stop()
{
if (not mRunning)
return;
mRunning = false;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
private:
std::atomic<bool> mRunning = false;
std::mutex mMutex;
std::thread mThread;
std::string mName;
};
然后,我使用这个自定义类的一个对象作为另一个“worker”类的成员,该类分配一个特定的函数来定期执行,如下所示
class Worker1
{
public:
void StartWorking()
{
mThread.Start([this]() { Work(); });
}
void StopWorking() { mThread.Stop(); }
private:
ThreadLoop mThread{"worker1 loop"};
void Work()
{
fmt::print("Working...\n");
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};
我把所有这些“工人”都放在另一个班级中,并在随机时间点调用/对他们进行调用(也在StartWorking()
StopWorking()
ThreadLoop
)
class Main
{
public:
void Start()
{
mThread.Start([this]() { MainLoop(); });
}
void Stop() { mThread.Stop(); }
private:
ThreadLoop mThread{"main loop"};
Worker1 mWorker1;
void MainLoop()
{
if (/*something*/)
mWorker1.StartWorking();
else
mWorker1.StopWorking();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
};
类中的第一个(“主循环”)启动正常,并开始调用 / ,正如预期的那样。然后触发 worker 启动自己的(“worker1 循环”),该循环在函数内部不确定地失败,例如通过ThreadLoop
Main
StartWorking()
StopWorking()
StartWorking()
ThreadLoop
ThreadLoop::Start()
`../nptl/pthread_mutex_lock.c:81: __pthread_mutex_lock: Assertion mutex->__data.__owner == 0 failed`
此外,根据调试器的说法,整个对象似乎未初始化/销毁(例如,变量是空的,尽管我总是提供一个非空字符串) - 这可能会导致失败 - 锁定未初始化/销毁的互斥锁。我的问题是,对象如何/为什么未初始化?我想我清楚地将它构造为每个对象内部的成员?ThreadLoop
std::string mName
std::scoped_lock
ThreadLoop
Worker1
答:
问题是我在里面的 lambda 中捕获了 by reference,然后线程调用了 lambda,这是一个悬空的引用。解决方法是按值捕获。T&& function
ThreadLoop::Start()
T&& function
原始版本:
mThread = std::thread([&](){ ... })
固定版本:
mThread = std::thread([this, function](){ ... })
更好的版本:
mThread = std::jthread([this, function](){ ... })
评论
Stop()
Main
Main
cout << "Destructor called for class X" << endl;
Stop
ThreadLoop
Worker
ThreadLoop