我正在阅读关于线程的一些manuals,我开始认为他们展示的代码并不安全:
std::cout << "starting first helper...\n";
std::thread helper1(foo);
std::cout << "starting second helper...\n";
std::thread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join(); // #1 NOT SAFE
helper2.join(); // #2 NOT SAFE
我相信这段代码不是绝对安全的。如果我没有误会,当控件到达标记为helper1
和helper2
的行时,无法保证#1
和#2
已处于可加入状态。线程仍然无法启动,此时没有任何ID。这将导致从std::thread::join()
我认为以下代码可以解决问题。我是对的吗?
std::cout << "starting first helper...\n";
std::thread helper1(foo);
std::cout << "starting second helper...\n";
std::thread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
while ( helper1.joinable() == false ) { }
helper1.join(); // #1 SAFE
while ( helper2.joinable() == false ) { }
helper2.join(); // #2 SAFE
答案 0 :(得分:5)
如果std::thread
包含尚未joinable
或join
编辑的主题状态,则detatch
为std::thread
。
move
通过非默认构造获得线程状态,或者从另一个std::thread
加入一个move
。 join
编辑时会丢失它。
构造完成后获得线程状态没有延迟。当线程函数完成时它不会消失。所以没有那个问题。
如果代码抛出上面,则会出现问题,您将无法detatch
或std::thread
,导致程序关闭时出现坏消息。始终将std::async
包装在RAII包装器中以避免这种情况,或者只使用返回void
的{{1}}并同样包装生成的std::future
(因为标准表示它在dtor中阻塞,但微软实现没有,所以你不能相信它是否会。)
答案 1 :(得分:3)
您以过于复杂的方式感知线程。 join
可以安全加入一个帖子。只需使用:
std::thread my_thread(my_main);
my_thread.join();
答案 2 :(得分:2)
std::thread::thread(F&& f, Args&&... args)
构造函数具有此后置条件:
后置条件:
get_id() != id()
。*this
代表新启动的主题。
joinable()
的定义是
返回:
get_id() != id()
因此构造函数的后置条件是对象是可连接的,并且后置条件在构造函数完成后立即应用。操作系统是否实际启动了线程无关紧要,thread
对象仍然知道新线程的ID,并且仍然可以等待它完成并加入它。