千家信息网

C++初级线程使用方法有哪些

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要讲解了"C++初级线程使用方法有哪些",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"C++初级线程使用方法有哪些"吧!1 线程启动C++11
千家信息网最后更新 2026年11月03日C++初级线程使用方法有哪些

这篇文章主要讲解了"C++初级线程使用方法有哪些",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"C++初级线程使用方法有哪些"吧!

1 线程启动

C++11中,线程的启动终究是对std::thread的对象进行构造。

线程构造的类别如下:

1.1 线程函数无参数无返回值

此类可以说是最简单的线程启动,函数不需要传参也不需要返回函数执行结果,执行完成后,线程自动退出。

形如:

void FunDoingNothing();std::thread(FunDoingNothing)

编写代码时,需要加上头文件以方便编译器能够正确处理thread对象。

1.2 线程函数有参数无返回值

C+=11中,thread的构造函数中使用了可变参数,这样,可以使得构造thread对象时可以自定义传入参数,

构造函数的定义如下:

template explicit thread(F&& f, Args&&... args);

在实际使用时,线程函数有参数时可以定义形式如下:

void printMsg(int a, int b) {  cout << "input params are:" << a <<","<

1.3 调用可调用的类型构造

使用时,可以将带有执行函数的变量传入thread的构造函数中从而替换默认的构造函数,

如下:

using namespace std;class BackGroundTask{ public:    void operator()() const{        doSomeThing();    }priavte:    doSomeThing();};int main(){    BackGroundTask f;    std::thread myThread(f);}

上面的代码中,在启动线程时同构构造对象f,f对象的重载函数中调用了线程运行时要执行的方法。但有一点需要注意的是,在传入临时的构造对象时,不经过处理,可能会让编译器产生错误的理解。

如:

std::thread myThread(BackGroundTask());

这里相当与声明了一个名为myTread的函数, 这个函数带有一个参数(函数指针指向没有参数并返回BackGroundTask对象的函数), 返回一个 std::thread 对象的函数, 而非启动了一个线程。

如果要解决这个问题,只需要如下处理即可:

std::thread myThread((BackGroundTask()));std::thread myThread{BackGroundTask()};

当然,也可以使用lamda表达式实现上述功能,如下:

std::thread myThread([]{  doSomeThing();  });

2 等待线程

C++11中,确保线程执行完后,主线程在退出,需要在代码中使用join()函数,这样就可以保证变量在线程结束时才会进行销毁。

2.1 join等待

在实际编程时,join函数只是简单的等待或者不等待。在有些场景下就会不使用,如果想要进行更加灵活的控制,需要使用C++11中提供的其他机制,这个也会在后面的推文中进行说明。
在编程时,如果对一个线程使用了join,那么在后续的操作中如果使用joinable()执行结果将返回false。既一旦使用了join。线程对象将不能重复使用。如下代码中,在线程中使用join等待。

class BackGroundTask{ public:    void operator()(){        doSomeThing();    } private:    void doSomeThing() {cout<<"线程退出"<

上面的代码使用了线程等待,可以输出正确的结果,如下:

线程退出
退出

如果将 myThread.join()语句注释,再次执行时,程序将执行出错,因为在子线程还没有结束时,主线程已经结束。

运行结果如下:

退出
terminate called without an active exception

上面的输出具备不确定性,代码运行时结果随机。

2.2 异常场景的join等待

异常场景中,如果没有充分考虑join的位置,就可能会产生因为异常导致主线程先于子线程退出的情况,解决这些问题可以通过下面两种方法进行处理:

2.2.1 通过异常捕获

通过分析代码中的异常场景,对异常使用try...catch进行捕获,然后在需要线程等待的地方调用join()函数,这种方法虽然可以轻易地捕获问题并对问题进行修复,但并非是通用法则,还需要根据实际情况进行分析。如检查并确认是否线程函数中是否使用了局部变量的引用等其它原因。

2.2.2 使用RAII方式进行线程等待

RAII可以理解为资源获取既初始化。因为全写为:Resource Acquisition Is Initialization。
实际使用时,通过定义一个类,然后在析构函数中使用join函数进行线程等待。这样可以避免场景有遗漏的地方。

class thread_guard{private:    std::thread& t;public:    explicit thread_guard(std::thread& t_):t(t_){}    ~thread_guard()    {        if(t.joinable())        {            t.join();        }    }     thread_guard(thread_guard const&)=delete;    thread_guard& operator=(thread_guard const&)=delete;};

如上,通过在将线程对象传入到类thread_guard中,如果thread_guard类对象的局部变量被销毁,则在析构函数中会将线程托管到原始线程。
在thread_guard中,使用delete标识,禁止生成该类的默认拷贝构造、以及赋值函数。
在实际编程时如果不想线程等待,可以使用detach方法,将线程和主线程进行分离。

3 线程分离

线程分离使用detach方法,使用后将不能在对已分离的线程进行管理,但是分离的线程可以真实的在后台进行运行。当线程退出时,C++会对线程资源进行清理和回收。
线程分离通常被用作守护线程或者后台工作线程。

使用方法如下:

int main(){    BackGroundTask f;    std::thread myThread(f);    myThread.detach();    cout<<"退出"<

4 向线程传递参数

向线程传递参数非常简单,在上面的代码中也有提及,这里主要说下向线程中传递参数的陷阱。

看下面的代码:

void f(int i,std::string const& s);void oops(int some_param){char buffer[1024]; sprintf(buffer, "%i",some_param);std::thread t(f,3,buffer);t.detach();}

上面的代码中buffer是一个局部指针变量,使用后,可能会导致线程出现未定义的行为,因为从char*到string的转换时使用的是隐式转换,但是thread在使用时会将变量拷贝到线程私有内存,但是并不知道需要将参数进行转换,因此复制到私有内存的变量就没有转换成期望的对象。
如果要解决这个问题,可以在使用时直接将参数类型转换成函数默认的类型,在上面的例子中可以

做如下操作:

std::thread t(f,3,std::string(buffer));

但是这样做依然存在问题,既线程在复制变量到私有内存时,只复制了变量值,这样在线程调用后,如果继续使用线程函数处理后的变量时可能变量并没有改造,依旧是线程调用之前的变量。
因此要想在函数传参过程中使得线程拷贝时依旧保持引用,可以在线程调用时使用引用方式,

如:

std::thread t(f,3,std::ref(std::string(buffer)));

5 线程识别

每个线程都有一个线程标识,在C++11中,线程标识通过std::thread::id进行标识,std::thread::id可以复用并进行比较,如果两个线程的id相等,那么它们就是同一个线程或者没有线程,如果不等就表示两个是不同的线程或者其中一个线程不存在。

线程id的获取方法有两种,如下:

5.1 thread成员函数获取

通过std::thread::get_id()可以获取线程的id。

使用方法如下:

int main(){    BackGroundTask f;    std::thread myThread(f);    cout<<"线程id:"<

线程运行结果为:

  • 线程id:139879559096064

  • 退出

5.2 std::this_thread::get_id()

线程id可以用来区分主线程和子线程,通过std::this_thread::get_id()可以先将主线程id保存,然后在和子线程进行比较,从而区分主线程和子线程。

代码如下:

int main(){    std::thread::id master_thread=std::this_thread::get_id();    BackGroundTask f;    std::thread myThread(f);    if(master_thread!=myThread.get_id())    {        cout<<"子线程id:"<

代码中,先保存了主线程的id标识,然后获取子线程id,比较两个线程id。如果不相等则输出子线程id。

代码运行结果如下:

子线程id:140161423791872

感谢各位的阅读,以上就是"C++初级线程使用方法有哪些"的内容了,经过本文的学习后,相信大家对C++初级线程使用方法有哪些这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

线程 函数 代码 方法 参数 对象 变量 面的 结果 问题 使用方法 运行 C++ 场景 实际 标识 处理 两个 内存 局部 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 湖南永感互联网科技有限公司 科技赋能下的互联网金融 互联网科技金融 上海互联网科技有限公司 互联网科技股票 互联网女皇 腾讯科技 衡水科技产业互联网平台 哈尔滨秒房互联网科技有限公司 富国互联网科技创新基金 深圳国际航运科技和互联网 团飞科技与伯堂互联网服务平台 栖莱科技互联网股份有限公司 互联网科技革命什么开始 长沙互联网科技 伊宁互联网科技做什么的 互联网时代科技型公司的机会 互联网科技手机排行榜 广西科技产业互联网是什么 广州三三互联网科技有限公司 众联互联网络科技 互联网与科技 小马过河互联网科技 银川新华互联网科技学校地址 剑昆互联网络科技有限公司临桂 互联网科技与大数据专业 互联网第三次科技革命特征 互联网科技融合创新 互联网时代科技型公司的机会 容联科技互联网电商 高科技和互联网的现代国家
0