理解 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 <iostream>
#include <thread>
#include <chrono>
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<chrono::seconds>(system_clock::now() - start).count();
    std::cout << "done! elapsed " << elapsed << " seconds.";
}

执行结果

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

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

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

执行示意图

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

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

好在record执行的时间短。

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

detach 的弊端(野线程)

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

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

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

#include <iostream>
#include <thread>
#include <chrono>
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<chrono::seconds>(system_clock::now() - start).count();
    std::cout << "done! elapsed " << elapsed << " seconds.";
}

执行结果

可以看到,没有出现 record finished

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

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

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

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

执行示意图

record 成了一个野线程,还没执行完,被系统强制回收

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

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

detach 的正确用法

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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值