![](/img/trans.png)
[英]wxWidgets - terminate called without an active exception (using std::thread)
[英]Terminate called in move assignment of std::thread
我有一个由多个用户使用的多线程应用程序。 对于某些用户,运行应用程序会导致
terminate called without an active exception
Aborted
使用 GDB 运行应用程序会产生以下 output:
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 class。 归功于 https://thispointer.com/c11-how-to-use-stdthread-as-a-member-variable-in-class/ 。 通过将其传递this
std::thread
的能力对其进行了扩展,因为我确实需要访问 WatchDog class。
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
class 中的 ThreadWrapper object 是否可以避免对Init
的潜在调用两次? 计划在 WatchDog 构造函数中初始化 threadwrapper_,但据我所知更多。 再次感谢大家。
threadwrapper_ = ThreadWrapper(&WatchDog::Log, this);
如果
*this
仍有关联的运行线程(即joinable() == true
),则调用std::terminate()
。
看起来您的t_
有一个关联的正在运行的线程,但您的非真实代码没有显示这一点。
为了建立现有的答案,最有可能发生的是您在同一个 object 上调用Init()
两次,这会导致分配给现有(可连接)线程,这是不允许的。 考虑使用新接口重新设计此 class。
Init()
应该隐式发生在构造上。Stop()
应该在销毁时隐式发生。Log()
私有且 const 正确。 使用此实现,不可能意外调用Init()
两次,并且在销毁时会自动进行清理。 (在您的实现中,如果您忘记调用Stop()
则线程将永远不会加入,因为run_
永远不会设置为 false。)
如果您希望能够拥有“可能处于活动状态的WatchDog
”,那么您可以简单地使用std::optional<WatchDog>
。
class WatchDog {
std::atomic<bool> run_;
std::thread t_;
public:
WatchDog();
~WatchDog();
private:
void Log() const;
};
WatchDog::WatchDog() :
run_{true},
t_{&WatchDog::Log, this} {}
void WatchDog::Log() const {
while (run_) {
std::cout << "Operational" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// throw;
}
}
WatchDog::~WatchDog() {
run_ = false;
if (t_.joinable()) {
t_.join();
}
}
使用此实现,您给定的main()
变为:
int main() {
WatchDog dog;
std::this_thread::sleep_for(std::chrono::seconds(2));
}
如果您想在更动态的设置中更明确地控制 object 的生命周期,这就是std::optional
的用武之地:
int main() {
std::optional<WatchDog> dog;
dog.emplace(); // Replaces Init()
std::this_thread::sleep_for(std::chrono::seconds(2));
dog.reset(); // Replaces Stop()
}
显然,这将具有与其他main()
示例相同的可观察行为,但重点是说明如果 object 的生命周期需要更复杂且不受值的生命周期的精确限制,您如何创建和销毁 object。
这解决了Init()
被调用两次的问题,因为std::optional::emplace()
将在创建新值之前破坏包含的值。 当然,如果您只想确保有一个活动的WatchDog
(而不是不必要地破坏和创建一个),那么您可以执行类似if (.dog) { dog;emplace(); }
if (.dog) { dog;emplace(); }
。
作为旁注,如果WatchDog::Log
从不使用this
,则可以将其设置为static
,然后特定线程不会绑定到特定的WatchDog
实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.