如何在退出前重新附加线程或等待线程完成

Posted

技术标签:

【中文标题】如何在退出前重新附加线程或等待线程完成【英文标题】:How to reattach thread or wait for thread completion before exiting 【发布时间】:2019-04-22 15:49:44 【问题描述】:

我有一个使用 asio 库连接到套接字的应用程序。我的主线程调用open() 到套接字实现,然后它与主线程分离以继续执行。

thread::detach() 被调用时,线程不能再被加入,因此调用者不知道线程何时完成执行。至少在我的应用程序中,这会导致不干净的关闭。资源未正确释放,并且仅在某些情况下会导致崩溃。是否可以重新加入分离的线程?我想尽量避免使用条件变量。

这是我的例子:

io_socket io = new io_socket();

// The call to thread::detach() is done within io_socket::open()
io->open();

// Let some things happen...
std::this_thread::sleep_for(std::chrono::seconds(20));

// Safely kills all operations and resources before delete
io->close();

// A hack solution to allow time for the detached thread to do what it needs to do
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));

// Crashes if I don't allow the detached thread to safely close
delete io;

return EXIT_SUCCESS;

【问题讨论】:

使用某种条件变量并让close 等待,直到所有分离的线程通知它们完成。 @NathanOliver 我应该把它放在我原来的帖子中,但如果可能的话,我会尽量避免使用条件变量。我希望有一个更干净的解决方案,比如加入一个分离的线程。虽然我知道目前的规范是不可能的。 要么提供自己的同步机制,要么不使用detach 我能问一下为什么线程首先被分离了吗?为什么没有io_socketstd::thread m_thread 成员并让io_socket 析构函数执行必要的清理? 【参考方案1】:

您无法重新附加已分离的std::thread。等待分离线程结束的唯一方法是使用某种原子/条件变量在它完成时通知您。

也就是说,您可能一开始就不需要分离线程。您可以做的是为线程创建存储(想到std::vector<std::thread>),然后在open 中将新线程添加到该存储。完成后,您调用close 并让close 遍历存储中的每个线程并在其上调用join。这将使close 成为您的同步点,之后您就知道所有线程都已结束,您可以安全地关闭应用程序。

【讨论】:

【参考方案2】:

假设 IO 线程是由你编写的,你可以通过 std::promisestd::future 的组合来处理这个问题,如下所示:

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

using namespace std::chrono_literals;

void demo_thread (std::promise <bool> *p)

    std::cout << "demo thread waiting...\n";
    std::this_thread::sleep_for (1000ms);
    std::cout << "demo thread terminating\n";
    p->set_value (true);


int main ()

    std::promise <bool> p;
    std::thread t = std::thread (demo_thread, &p);
    t.detach ();

    // ...

    std::cout << "main thread waiting...\n";
    std::future <bool> f = p.get_future();
    f.wait ();

    std::cout << "main thread terminating\n";

Live demo

【讨论】:

这是我一直在寻找的答案。我对 C++ 期货没有太多经验,但这看起来是我想要的方向。谢谢。【参考方案3】:

是否可以重新加入分离的线程?

没有。这将破坏detach() 的全部目的。

t.detach() 调用实际上并没有做任何必要的事情。它的唯一目的是禁用安全机制。

通常,如果您没有detach() 一个线程,那么如果您允许在程序拥有join()ed 之前销毁该对象,那么thread 对象的析构函数将引发错误。这样做的唯一目的是帮助您避免一个常见错误:它旨在防止您的 main() 例程在所有其他线程完成之前退出并终止程序。

t.detach() 的目的---唯一目的---是告诉图书馆,“谢谢,但我知道我在做什么,我不需要帮助,而我绝不会打电话给t.join()


如果您希望您的程序调用 t.join(),请不要调用 t.detach()

【讨论】:

【参考方案4】:

NathanOliver 提供了一个很好的答案,但如果 io-&gt;open() 函数不返回对它创建的 std::thread 对象的引用(如果它分离,它可能不会),他的解决方案可能会很困难。如果您直接使用 Asio 库,我希望 io-&gt;close() 应该在删除 io 之前正确处理线程的正常退出。但是,如果这是您实现的包装器,您需要返回对open() 中创建的线程对象的引用,或者更改close() 的实现,使其阻塞,直到资源实际被释放。此外,如果这些是您自己实现的方法,那么open() 中的分离是否还有必要?

【讨论】:

以上是关于如何在退出前重新附加线程或等待线程完成的主要内容,如果未能解决你的问题,请参考以下文章

在退出进程时等待线程完成

Java如何实现线程的暂停和重新启用?求大神

Java中的守护线程

c++多线程在异常环境下的等待

C ++如何等待在另一个线程上执行的方法然后主线程完成(VS2010)

如何不等待 OpenMP 中的其他线程?