簡體   English   中英

c++ std::thread join() 在線程超出 scope 之后

[英]c++ std::thread join() after thread is out of scope

我在這里第一次嘗試真正的問題。 我四處搜索,但沒有得到一個好的答案,所以如果這是一個 LMGTFY 問題,我深表歉意。

我正在為 c++ 應用程序引擎開發運行時循環。 這是一個標准的運行循環,我想在它自己的線程上執行,所以我可以控制執行時間而不依賴於操作系統特定的依賴項。 但是,我仍然想讓應用程序 window 與操作系統(在這種情況下為 Mac OSX)配合得很好,所以我有一個 objective-C 平台 object 將鍵/鼠標輸入傳遞給我的應用程序。 我的意圖是讓引擎通過原子 arrays 收集輸入,然后應用程序可以適當地響應來自操作系統的 Cocoa 回調,但我仍然可以保留對游戲循環的控制。

我遇到的問題是我當前的設置涉及一個啟動線程的方法,以及一個用於加入線程的單獨線程,因此達爾文可以對線程如何終止感到滿意。

//This is all in a class named engine.
id thread; // What type?
bool engineRunning;

int Engine::startYourEngines() {
        engineRunning = true;
        std::thread thread (engineLoop()); //How do I keep this thread?
        
    }

    int Engine::stopYourEngines() {
        engineRunning = false;
        thread.join(); //Thread is out of scope...or is it?
        return 0;
    }
    
    int Engine::engineLoop() { //Only invoke this with Engine::startYourEngines()
        engineThread = std::this_thread::get_id();
        
        do {
            // do things here!
            }
        } while (engineRunning);
        
        return 0;
    } /* Engine::engineLoop() */

我發現的所有示例都使用單個 function 來啟動線程並加入它。 我不想拆線,因為那只是一罐蠕蟲。 看起來獲取線程ID是一種識別線程的方法,但是我不知道如何使用ID再次找到線程。 這似乎是做我想做的最直接的方法。

似乎您想讓 object Engine啟動一個工作thread ,並保存對該上下文的引用,以便在關閉時可以巧妙地關閉它。 如果您正在使用 C++20,您將使用jthreadstop_token來獲得超級整潔的 RAII。 如果您不使用 20(很可能),那么您必須自己動手。

我將此稱為停止標記習語,因為這就是 C++20 概念的名稱。 (我假設)您想要執行以下操作:

  • 創建一個整潔的Engine class 來處理工作線程的輸入和 output。
  • 在創建Engine時,打開您的工作線程,然后
  • 保持運行直到Engine損壞。

如果您必須使用初始化類型方法 ( startYourEngines ) 和拆卸類型方法 ( stopYourEngines ),那么可能的解決方案如下所示。

// each start requires a paired stop
// may not double-start or double-stop
// starting and stopping are not thread-safe (may not run at the same time in different threads)
class Engine {
public:
    Engine() : run_engine_{false}, worker_{} {}

    void startYourEngines() {
        run_engine_ = true; // tell the engine it's okay to run
        worker_ = std::thread([&]{ engineLoop(); }); // start running the engine
    }

    void stopYourEngines() { // changed return type to void since you're not returning anything
        run_engine_ = false; // tell the engine to stop running
        worker_.join(); // wait for engine to stop running
    }

    void engineLoop() { // changed return type to void since you're not returning anything
        while (run_engine_) {
            // do things here!
        }
    }
};

在上面,每個startYourEngines調用必須與stopYourEngines調用配對。 您不能雙重啟動或雙重停止引擎,也不能同時調用startYourEnginesstopYourEngines 有一些方法可以允許從不同線程開始和停止調用,但更容易禁止開發人員這樣做。

我個人覺得“init”和“teardown”風格的函數很可惡。 由於您的代碼最終看起來像

Engine engine;
engine.startYourEngines();
...do program stuff here until ready to shutdown...
engine.stopYourEngines();

一個更簡潔的替代方案是合並 RAII 風格的代碼,其中構造Engine創建執行線程,而解構Engine將其拆毀。 但是以上內容應該足以讓您繼續前進。

經過一天的折騰,這就是我想出的可行解決方案。

#include <iostream>
#include <thread>
#include <chrono>

class Engine {
public:
    bool engineRunning = true;
    void engineLoop() {
        do {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            std::cout << "engine running\n";
        } while (engineRunning);
    }
    std::thread engineThread;
    Engine() {
        engineThread = std::thread(&Engine::engineLoop, this);
    }
    ~Engine() {
        engineRunning = false;
        if (engineThread.joinable()) engineThread.join();
    }
};

int main(int argc, const char * argv[]) {
    std::cout << "start program\n";
    Engine *testThread = new Engine;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "kill engine \n";
    testThread->engineRunning = false;
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Done!\n";
    return 0;
}

感謝您的評論和幫助! 隨意評論上面的代碼! 我總是想變得更好!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM