【发布时间】:2021-05-15 04:38:28
【问题描述】:
我有一个由多个用户使用的多线程应用程序。对于某些用户,运行应用程序会导致
terminate called without an active exception
Aborted
使用 GDB 运行应用程序会产生以下输出:
Thread 1 ... received signal SIGABRT, Aborted.
__GI__raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) where
#0 _GI_raise (sig=sig@entry=6) at ./sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007f46925e9921 in GI_abort () at abort.c:79
#2 0x0000744692404957 in ?? (from /usr/lib/x86_64-linux-gnu/libstdc++.50.6
#3 0x00007F4692fe2ae6 in ?? (from /usr/lib/x86_64-linux-gnu/libstdc++.50.6
#4 0x00007F4692fe2b21 in std::terminate() ()
from /usr/lib/x86_64-linux-gnu/libstdc++.50.6
#5 0X000056407cb17783 in std::thread::operator=(std::thread&&) ()
...
在线查看,错误似乎是由于处理线程清理不当造成的(其中一个线程仍可连接)。下面的代码是在应用程序中找到的代码示例。
看门狗
class WatchDog {
std::thread t_;
std::atomic<bool> run_;
public:
WatchDog(){};
void Init() { t_ = std::thread(&WatchDog::Log, this); }
void Log() {
run_ = true;
while (run_) {
std::cout << "Operational" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// throw;
}
}
void Stop() { run_ = false; }
~WatchDog() {
if (t_.joinable())
t_.join();
}
};
主要
int main() {
WatchDog dog;
dog.Init();
std::this_thread::sleep_for(std::chrono::seconds(1));
dog.Stop();
}
示例剥离的应用程序运行无故障,并且在实际应用程序中也遵循了 RAII 习惯用法。不过,仔细回顾 GDB 结果,似乎终止调用是在移动赋值构造函数本身中对t_ 本身进行的。关于如何发生这种情况的任何解释以及调试它的建议?感谢您的帮助。
编辑
谢谢,Slava、BitTickler、cdhowie.. 我不知道 std::optional。我注意到我在其他几个地方犯了设计错误,所以想创建一个 ThreadWrapper 类。归功于https://thispointer.com/c11-how-to-use-stdthread-as-a-member-variable-in-class/。通过将this 传递给std::thread 的能力对其进行了扩展,因为我确实需要访问 WatchDog 类。
class ThreadWrapper {
public:
// Delete copy constructor
ThreadWrapper(const ThreadWrapper &) = delete;
// Delete assignment constructor
ThreadWrapper &operator=(const ThreadWrapper &) = delete;
// Parameterized Constructor
template <class F, class... Args>
explicit ThreadWrapper(F&& func, Args &&... args)
: thread_(std::forward<F>(func), std::forward<Args>(args)...) {}
// Move constructor
ThreadWrapper(ThreadWrapper &&obj) : thread_(std::move(obj.thread_)) {}
// Move Assignment Constructor
ThreadWrapper &operator=(ThreadWrapper &&obj) {
if (thread_.joinable()) {
thread_.join();
}
thread_ = std::move(obj.thread_);
return *this;
}
~ThreadWrapper() {
if (thread_.joinable()) {
thread_.join();
}
}
private:
std::thread thread_;
};
在 WatchDog 类中保存的 ThreadWrapper 对象现在是否可以安全地避免对 Init 的两次潜在调用?计划在 WatchDog 构造函数中初始化 threadwrapper_,但据我所知更多。再次感谢大家。
threadwrapper_ = ThreadWrapper(&WatchDog::Log, this);
【问题讨论】:
-
此示例代码是否在用户计算机上出现相同的故障?如果不是,则删除的代码可能是相关的。
-
我们无法真正帮助您修复有效的代码。我们可以得到一个真正重现问题的minimal reproducible example 吗?
-
如果你像这样重写
void Init() { t_ = std::thread(&WatchDog::Log, this); }会发生什么:void Init() { t_ = std::move(std::thread(&WatchDog::Log, this)); }? -
@BitTickler 功能相同
-
禁用
WatchDog类的移动构造函数和移动赋值运算符,看看是否出现编译错误。很可能在代码中的某处分配给WatchDog类本身
标签: c++ multithreading terminate