![](/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.