c++ std::thread join()在线程超出作用域后



我在这里第一次尝试一个真正的问题。我四处搜索了一下,但还没有得到一个好的答案,所以如果这是一个LMGTFY问题,我很抱歉。

我正在为一个c++应用程序引擎的运行时循环工作。这是一个标准的运行循环,我想在它自己的线程上执行,所以我可以控制执行时间,而不依赖于操作系统特定的依赖关系。然而,我仍然想让应用程序窗口在操作系统上运行良好,在本例中是Mac OSX,所以我有一个objective-C平台对象,将键/鼠标输入传递给我的应用程序。我的意图是让引擎通过原子数组收集输入,然后应用程序可以适当地响应来自操作系统的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() */

我找到的所有示例都使用单个函数来启动线程并加入它。我不想把这条线拆开,因为那只是一堆麻烦。似乎获得线程ID是识别线程的一种方法,但后来我不知道如何使用ID再次找到线程。那似乎是做我想做的事最直接的方法。

似乎您想让对象Engine启动工作程序thread,并保持对该上下文的引用,以便在关闭时可以整齐地关闭它。如果您正在使用c++ 20,您将使用jthreads和stop_tokens来实现超级简洁的RAII。如果你不使用20(很有可能),那么你必须自己滚动。

我将称其为停止标记习语,因为这是c++ 20概念的名称。(我假设)你想做以下事情:

  • 创建一个简洁的Engine类来处理工作线程的输入和输出。
  • 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_init_;"one_answers";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 runningn";
} 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 programn";
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;
}

谢谢你的评论和帮助!请随意评论上面的代码!我总是想要变得更好!