考慮以下代碼,我在其中宣告了一個用于執行異步/執行緒操作的簡單類:
#include <chrono>
#include <thread>
#include <mutex>
#include <future>
#include <iostream>
using namespace std::chrono_literals;
class basic_executor {
public:
basic_executor() {
_mx.lock();
printf("Ctor @%p\n", this);
_mx.unlock();
}
virtual ~basic_executor() {
_mx.lock();
printf("Dtor @%p\n", this);
_mx.unlock();
if (_thread.joinable()) {
_thread.join();
_mx.lock();
printf("Joined thread @%p\n", this);
_mx.unlock();
}
}
// sync call
void run() {
start();
execute();
stop();
}
// async call
void launch(bool detach = false) {
// create packaged task
std::packaged_task< void() > task([this] {
start();
execute();
stop();
});
// assign future object to function return
_done = task.get_future();
// launch function on a separate thread
_thread = std::thread(std::move(task));
// detach them from main thread in order to avoid waiting for them
if (detach == true) {
_thread.detach();
}
}
// blocking wait for async (detached/joinable)
void wait() const {
_done.wait();
}
protected:
virtual void start() { /* for derived types to implement */ }
virtual void stop() { /* for derived types to implement */ }
virtual void execute() { /* for derived types to implement */ }
std::mutex _mx;
std::thread _thread;
std::future< void > _done;
};
并使用以下應用程式示例,我從中派生出兩個記錄器物件,這些物件在特定時間段內進行虛擬列印:
class logger : public basic_executor {
public:
logger() { /* ... */}
~logger() {
_mx.lock();
std::cout << "logger destructor " << std::endl;
_mx.unlock();
}
void execute() override {
std::this_thread::sleep_for(1s);
for (int i = 0; i < 10; i) {
_mx.lock();
printf("L1: I am printing something\n");
_mx.unlock();
std::this_thread::sleep_for(1s);
}
}
void stop() override {
_mx.lock();
printf("L1: I am done!\n");
_mx.unlock();
}
};
class logger2 : public basic_executor {
public:
logger2() { /* ... */}
~logger2() {
_mx.lock();
printf("logger2 destructor\n");
_mx.unlock();
}
void execute() override {
for (int i = 0; i < 10; i) {
_mx.lock();
printf("L2: I am ALSO printing something\n");
_mx.unlock();
std::this_thread::sleep_for(2s);
}
}
void stop() override {
_mx.lock();
printf("L2: I am done!\n");
_mx.unlock();
}
};
int main(int argc, char const *argv[]) {
/* code */
// printf("log:\n");
logger log1;
// printf("log1:\n");
logger2 log2;
printf("----------------------------------!\n");
log2.launch();
log1.launch();
// log1.wait();
// log2.wait();
printf("----------------------------------!\n");
return 0;
}
我從程式中得到一個意想不到的行為:
Ctor @0x7fff8b18c990
Ctor @0x7fff8b18c9e0
----------------------------------!
----------------------------------!
logger2 destructor
Dtor @0x7fff8b18c9e0
Joined thread @0x7fff8b18c9e0
logger destructor
Dtor @0x7fff8b18c990
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
L1: I am printing something
Joined thread @0x7fff8b18c990
在偶爾的“的log 2”的物件從未被銷毀前就開始執行它,或“加入()”在其解構式無限期掛起電話。發生這種情況是否有任何明顯的原因,我到底錯過了什么?
uj5u.com熱心網友回復:
該錯誤可能發生在任一日志記錄類中。但是,對于未定義的行為,您沒有任何保證,也不期望任何型別的一致結果。到目前為止,您僅在兩個日志記錄類之一中觀察到了相同的錯誤。雖然我可以解釋為什么會這樣,但實際上,這并不重要。該錯誤可能發生在任何一個物件上。讓我們從這里開始:
_thread = std::thread(std::move(task));
在此代碼繼續執行并從 回傳之前,您不會得到任何保證,即新的執行執行緒將立即開始執行以下任何操作launch():
std::packaged_task< void() > task([this] {
start();
execute();
stop();
});
大多數時候,實際上,這將在新的執行執行緒中開始運行得非常快。但你不能依賴它。C 向您保證的是,在完成構建新的執行執行緒后的 某個時刻std::thread將開始運行。它可能是立即的。或者,可能是幾百毫秒之后,因為您的作業系統有更重要的事情要做。
您期望新的執行執行緒將始終“立即”開始執行,同時進行std::thread構建。那不是真的。畢竟,您可能正在使用單個 CPU 內核運行,并且在構造std::thread物件之后,您將繼續在同一個執行執行緒中執行以下內容,并且僅在一段時間后發生背景關系切換,到新的執行執行緒。
同時:
launch() 回傳。父執行執行緒到達
main().自動作用域中的所有物件都將被一個一個地銷毀。
在 C 中,當一個物件由一個超類和一個子類組成時,子類首先被銷毀,然后是超類。這就是 C 的作業方式。
因此,
logger/logger2子類的解構式會立即被呼叫并銷毀其物件(只是logger/logger2子類)。現在呼叫超類的解構式來銷毀超類。
~basic_executor開始做它的事情,耐心等待。現在,最后,那個新的執行執行緒,還記得那個嗎?猜猜是什么:它終于開始運行,并勇敢嘗試執行
start(),execute()和stop()。或者,它首先設法通過了start(),但尚未到達execute()。但是既然實際的記錄器子類現在已經被銷毀了,你猜會發生什么?沒有。它消失了。子類不再存在。它不再是。它加入了隱形合唱團。它渴望峽灣。這是一個前子類。沒有logger::execute或logger2::execute()更多。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/349084.html
上一篇:如何解決多執行緒靜態變數遞增?
