C++多线程怎么实现_C++11标准库thread使用入门【指南】

2次阅读

直接用 std::Thread 启动线程,需手动调用 join() 或 detach(),否则析构时会调用 std::terminate() 导致程序崩溃;参数默认值传递,传引用需用 std::ref();成员函数需传对象指针或引用;异常不自动传播,需手动处理。

C++多线程怎么实现_C++11标准库thread使用入门【指南】

直接用 std::thread 就行,不用自己封装线程管理或调用系统 API;但必须手动处理 join()detach(),否则程序会崩溃。

怎么启动一个最简单的线程

把函数(或可调用对象)传给 std::thread 构造函数即可。注意:函数参数是**值传递**,如果要传引用得用 std::ref() 包裹。

  • std::thread t(func); —— 启动后立即运行,不阻塞主线程
  • std::thread t(func, arg1, arg2); —— 参数按值拷贝,arg1 在新线程里是副本
  • std::thread t(func, std::ref(x)); —— 显式传引用,避免意外拷贝大对象或需要修改原变量
  • 如果 func 是成员函数,第一个参数必须是对象指针或引用:std::thread t(&MyClass::method, &obj, arg)

线程结束前必须显式处理 join/detach

std::thread 对象析构时,若仍处于“可加入”(joinable)状态,会直接调用 std::terminate() —— 程序立刻退出,无任何提示。

  • t.join(); —— 主线程等待该线程结束,适合需同步结果的场景
  • t.detach(); —— 把线程设为后台运行,与 std::thread 对象解绑;之后不能再对它做任何操作
  • 常见错误:std::thread t(func); return; —— 函数返回导致局部 t 析构,而它还没被 joindetach,必崩
  • 安全做法:在作用域末尾加 if (t.joinable()) t.join();,或者用 RAII 封装(如 scoped_thread 类)

传递参数时容易踩的坑

构造 std::thread 时,参数会被完美转发,但默认行为是**拷贝**;若目标函数接收的是左值引用,又没用 std::ref,编译会失败或行为异常。

立即学习C++免费学习笔记(深入)”;

  • 错误写法:void f(int& x) { x = 42; } int a = 0; std::thread t(f, a); —— a 被拷贝,f 修改的是副本,a 不变
  • 正确写法:std::thread t(f, std::ref(a)); —— 修改生效
  • Lambda 捕获也一样:用 [&x]() { x = 42; } 捕获引用,否则捕获的是副本
  • 移动语义可用:std::thread t([](std::String s) { /*...*/ }, std::move(s));,避免冗余拷贝

别忘了线程局部存储和异常传播限制

std::thread 本身不传播异常——子线程里抛出的异常不会自动传到主线程,必须自己处理(比如用 std::promise/std::future 手动传递错误信息)。

  • 线程局部变量thread_local 声明,每个线程一份独立副本,生命周期随线程结束而销毁
  • 不要在线程函数里裸 throw 异常,除非你确定能 catch 到;更稳妥的是用 try/catch 包裹整个线程函数体
  • 全局对象的构造/析构可能跨线程竞争,尤其涉及静态局部变量时,c++11 保证其首次访问是线程安全的,但后续访问不保证

真正麻烦的不是怎么开线程,而是资源归属、生命周期管理和数据竞争——std::thread 只负责执行,其余都得你自己兜底。

text=ZqhQzanResources