理解 std::thread::detach

C++多线程并发编程入门(目录)

detach 的作用

detach 的作用就是让线程独自执行。

为何需要 detach

在 理解 std::thread::join 中,我们看到了,如果所有线程都是一开始就在 main 函数中创建好的,那么只需要有一个 join 函数就足够了。

但是 在 理解 std::thread::join 中,我们也已经发现了线程嵌套时,join的弊端。

为了解决 join的弊端,detach 也就出现了。

也就是 detach 仅仅是让线程独立执行,并不会让调用它的线程等待它。

这样线程运行起来之后,就和其他任何线程都没有了瓜葛。

各自独立放飞自我,没人管,没人问。

代码验证(detach)

我们将 理解 std::thread::join 中的第2个示例代码,record 线程的执行由 join 改为 detach。

#include 
#include 
#include 
using namespace std;
using namespace std::chrono;

void record()
{
    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(1));
    cout << "record finished!" << endl;
}

void ui_fun()
{
    std::cout << "starting record ...\n";
    std::thread record_thread(record);
    record_thread.detach();

    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(5));
    cout << "ui_fun finished!" << endl;
}

int main()
{
    auto start = std::chrono::system_clock::now();

    std::cout << "starting ui_fun ...\n";
    std::thread helper1(ui_fun);

    std::cout << "waiting for ui_fun to finish..." << std::endl;
    helper1.join();

    auto elapsed = chrono::duration_cast(system_clock::now() - start).count();
    std::cout << "done! elapsed " << elapsed << " seconds.";
}

执行结果

理解 std::thread::detach_第1张图片

可以看到,由于 UI线程没有等待 record 线程,所以, record 是在 UI线程执行的过程中自己独立执行完成的。

从而两个线程 ui_fun record 同时执行,整个程序执行的时间就变短了。

而此时 main 只是在等待 ui_fun线程的退出。

执行示意图

理解 std::thread::detach_第2张图片 只有main线程在等待UI线程,record是一个野线程

此时,只有main线程在等待UI线程,record是一个野线程。

好在record执行的时间短。

如果record执行的时间比ui_fun线程时间长呢?比如,窗口关闭了,录制写文件由于比较慢,还没完成。那岂不是要丢数据了?

detach 的弊端(野线程)

下面我们就来模拟 record 线程确实是比较慢,UI线程都被用户关闭窗口了,还没有把所有的录制数据写入到本地文件。

代码验证(detach野线程执行耗时操作)

完整代码:我们把 record 线程的执行时间由 原来的 1秒 改为 11秒。

#include 
#include 
#include 
using namespace std;
using namespace std::chrono;

void record()
{
    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(11));
    cout << "record finished!" << endl;
}

void ui_fun()
{
    std::cout << "starting record ...\n";
    std::thread record_thread(record);
    record_thread.detach();

    // simulate expensive operation
    std::this_thread::sleep_for(std::chrono::seconds(5));
    cout << "ui_fun finished!" << endl;
}

int main()
{
    auto start = std::chrono::system_clock::now();

    std::cout << "starting ui_fun ...\n";
    std::thread helper1(ui_fun);

    std::cout << "waiting for ui_fun to finish..." << std::endl;
    helper1.join();

    auto elapsed = chrono::duration_cast(system_clock::now() - start).count();
    std::cout << "done! elapsed " << elapsed << " seconds.";
}

执行结果

理解 std::thread::detach_第3张图片 可以看到,没有出现 record finished

我们可以看到,main 线程等到UI线程执行完就退出了。

main退出意味着整个进程退出,操作系统会回收一切。

这个时候 record 线程虽然还没执行完,但是也被操作系统(强拆)回收了。

这太糟糕了。程序没有正常的退出。

执行示意图

理解 std::thread::detach_第4张图片 record 成了一个野线程,还没执行完,被系统强制回收

所以,main还是要等待所有线程退出的。

作为所有模块最后退出的保障,main必须是最后一个退出才行。

detach 的正确用法

通过上面的案例分析,我们可以知道 detach 线程函数内部必须要要是一个无限循环。

并且有退出条件,而退出条件必须由外部设置。

同时,detach 线程内部要一直等待这个退出条件。

main 函数需要增加代码来触发这个 detach 线程的退出条件。从而变成了 detach 在启动的时候是以 detach 方式启动(不阻塞其他任何线程),同时 main 以代码的方式 等待(join)这个detach 线程。

由于具体的代码比较复杂,这里就不做完整的展示了。

我会在 本专栏 C++多线程并发编程入门(目录) 的后续文章中展示。

你可能感兴趣的:(c++,算法,开发语言,数据结构,java)