简体   繁体   English

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

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

my first attempt at a real question in here.我在这里第一次尝试真正的问题。 I've searched around, but haven't gotten a good answer to come up, so apologies if this is a LMGTFY question.我四处搜索,但没有得到一个好的答案,所以如果这是一个 LMGTFY 问题,我深表歉意。

I am working on a runtime loop for a c++ application engine.我正在为 c++ 应用程序引擎开发运行时循环。 It's a standard run loop, that I want to execute on it's own thread, so I can control the execution timing without relying on OS specific dependencies.这是一个标准的运行循环,我想在它自己的线程上执行,所以我可以控制执行时间而不依赖于操作系统特定的依赖项。 However, I want to still have the app window play nice with os, in this case Mac OSX, so I have an objective-C platform object that passes key/mouse input to my application.但是,我仍然想让应用程序 window 与操作系统(在这种情况下为 Mac OSX)配合得很好,所以我有一个 objective-C 平台 object 将键/鼠标输入传递给我的应用程序。 My intent is to have the engine collect input through atomic arrays, and then the app can appropriately respond to Cocoa callbacks from the OS, but I can still retain control over the game loop.我的意图是让引擎通过原子 arrays 收集输入,然后应用程序可以适当地响应来自操作系统的 Cocoa 回调,但我仍然可以保留对游戏循环的控制。

The problem I'm running into is that my current setup involves a method for starting the thread, and a separate thread for joining the thread so Darwin can be happy with how the threads all terminate.我遇到的问题是我当前的设置涉及一个启动线程的方法,以及一个用于加入线程的单独线程,因此达尔文可以对线程如何终止感到满意。

//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() */

All of the examples I find use a single function for launching the thread and joining it.我发现的所有示例都使用单个 function 来启动线程并加入它。 I don't want to detach the thread because that's just a can of worms.我不想拆线,因为那只是一罐蠕虫。 It seems like getting the thread ID is a way to identify the thread, but then I don't know how to use the ID to find the thread again.看起来获取线程ID是一种识别线程的方法,但是我不知道如何使用ID再次找到线程。 That seems to be the most straightforward way to do what I want to do.这似乎是做我想做的最直接的方法。

It seems like you want to have an object Engine launch a worker thread , and hold the reference to that context so it can be neatly closed when its time to shutdown.似乎您想让 object Engine启动一个工作thread ,并保存对该上下文的引用,以便在关闭时可以巧妙地关闭它。 If you're working with C++20 you'd use jthread s and stop_token s for super neat RAII.如果您正在使用 C++20,您将使用jthreadstop_token来获得超级整洁的 RAII。 If you're not using 20 (which is likely), then you have to roll your own.如果您不使用 20(很可能),那么您必须自己动手。

I will call this the stop token idiom since that's what the C++20 concept is called.我将此称为停止标记习语,因为这就是 C++20 概念的名称。 (I assume) you want to do the following: (我假设)您想要执行以下操作:

  • Create a neat Engine class that handles input and output to the worker thread.创建一个整洁的Engine class 来处理工作线程的输入和 output。
  • On Engine creation, open your worker thread, and在创建Engine时,打开您的工作线程,然后
  • keep it running until Engine destruction.保持运行直到Engine损坏。

If you must use an init-type method ( startYourEngines ) and a teardown-type method ( stopYourEngines ), then a possible solution would look like this.如果您必须使用初始化类型方法 ( 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!
        }
    }
};

In the above, each startYourEngines call must be paired with a stopYourEngines call.在上面,每个startYourEngines调用必须与stopYourEngines调用配对。 You may not double-start or double stop your engines, and you may not call startYourEngines and stopYourEngines concurrently.您不能双重启动或双重停止引擎,也不能同时调用startYourEnginesstopYourEngines There are ways to allow start and stop calls from different threads, but it's easier to just forbid the developer from doing this.有一些方法可以允许从不同线程开始和停止调用,但更容易禁止开发人员这样做。

I personally find the "init" and "teardown" style functions odious.我个人觉得“init”和“teardown”风格的函数很可恶。 Since your code ends up looking like由于您的代码最终看起来像

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

A neater alternative is to incorporate RAII-style code, where constructing an Engine creates the thread of execution, and deconstructing an Engine tears it down.一个更简洁的替代方案是合并 RAII 风格的代码,其中构造Engine创建执行线程,而解构Engine将其拆毁。 But the above should be enough to get you going.但是以上内容应该足以让您继续前进。

After a day of hacking around, here's what I came up with as a workable solution.经过一天的折腾,这就是我想出的可行解决方案。

#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;
}

Thank you for the comments and help!感谢您的评论和帮助! Feel free to comment on the code above!随意评论上面的代码! I always want to get better!我总是想变得更好!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM