c++线程概述

1.概览

  • 线程创建之后,必须使用join待其结束,否则资源无法回收,变成僵尸线程占据资源;当然也可以设置成detach方式,让线程自动回收资源
  • 进程也是一样的操作,子进程并不会自动回收资源,是需要手动回收,这样的目的是因为进程之间往往有父子关系,涉及到流程上的同步

2. 正文

2.1 线程中为什么有join?

thread::join()的一个作用就是阻塞直到线程执行完;
线程的使用源于异步并发的需求,如下需要把taskA()和taskB()的计算结果加起来,但假设它们都是耗时的任务,可以通过线程的方式同时跑两个任务;
taskB()运行结束后,threadTask.join()的作用是阻塞等待直到线程的taskA执行完毕,都执行完才能将两者的结果相加;

#include<thread>

int taskAResult = 0;
int taskBResult = 0;

void taskA(){
	taskAResult = 1 + 2;
}

void taskB(){
	taskBResult = 3 + 4;
}

int main(){

	std::thread threadTask(taskA);
	taskB();
	threadTask.join();
	int final = taskAResult + taskBResult;
	
retrun 0;
}
  • 如果使用场景和上面不同,创建线程后是否可以不join?

答案是不行, 出于使用多线程大多数会涉及到数据和流程上的关联,std::thread设计就显示要求了thread对象一定要join,如果不join,在c++thread 对象析构时,会主动raise abort()导致崩溃

~thread(){
	if(joinable()) //线程仍然可以join但是却析构了
		std::terminate();
}
  • join()是否有其它作用?
    有,举linux而言,linux是通过创建进程的方式创建线程(Light Weight Process),只不过线程共享了进程的空间,所以除了内存等资源之外,创建的线程会占用进程表(process table),如果不使用join(),线程的资源将无法回收,产生僵尸线程, 系统资源被占用积累多了,会运行卡顿,且无法再创建新的线程。
    僵尸线程目前在linux上貌似无法在bash上简单的查到,不知道如何排查, todo

2.2 能否不使用join()?

能,可以通过thread::detach(),将线程设置成detach状态,这样就不用做join()操作,也会自动回收资源;使用detach()后,线程的运行和结束就不可控了,要尤为注意是否访问了别的可能析供的资源(对象,成员,内存等),

#include<thread>

int taskAResult = 0;

void taskA(){
	taskAResult = 1 + 2;
}

int main(){
	std::thread threadTask(taskA);
	threadTask.detach();
retrun 0;
}

2.3 现代c++更好的工具?

c++20中增加了std::jthread, 其通过了RAII 在jthread对象析构的时候会自动调用join

#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
 
using namespace std::literals;
 
void f1(int n)
{
    for (int i = 0; i < 5; ++i)
    {
        std::cout << "Thread 1 executing " << n <<std::endl;
        ++n;
        std::this_thread::sleep_for(1s);
    }
}
 

int main()
{
    std::jthread t1(f1, 0);
    //t1.join()
    return 0;
}

可以看到,主线程运行jthread后并没有显示执行join(),但f1还是运行完成了, (当然也可以显示调用,join过的jthread,析构的时候不会产生重复调用join()的异常)

Thread 1 executing 0
Thread 1 executing 1
Thread 1 executing 2
Thread 1 executing 3
Thread 1 executing 4

当然jthread也和thread一样支持detach,jthread还有哪些新东西?

在循环的任务中,通常会使用一个变量让线程能退出循环,而jthread则引入了std::stop_token用来控制线程循环结束

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string_view>
#include <thread>
using namespace std::chrono_literals;
 
int main()
{
    auto stop_worker = std::jthread([](std::stop_token stoken)
    {
        int i = 0;
        while(!stoken.stop_requested()){
            std::this_thread::sleep_for(10ms);
            std::cout << "worker is running: "<< ++i << std::endl;
        }
        std::cout << "worker is stop\n";
    });
 
   
    std::stop_token stop_token = stop_worker.get_stop_token();
    std::this_thread::sleep_for(50ms);
    stop_worker.request_stop();
 
    return 0;
}

3. ref

【Linux】线程概念 && 线程控制(接口详谈)&& 僵尸线程的产生和解决: https://blog.csdn.net/Sober_harmonic/article/details/124929230

cppreference std::jthread: https://en.cppreference.com/w/cpp/thread/jthread

cppreference std::stop_token: https://en.cppreference.com/w/cpp/thread/stop_token/stop_requested

posted @ 2024-01-14 00:01  woder  阅读(16)  评论(0编辑  收藏  举报