std::thread 的创建方式、join 与 detach 的语义、传参陷阱与常见坑。同步机制(互斥锁、条件变量、原子操作)见另一篇并发笔记。

核心概念速查

概念说明关键点
进程独立运行的程序实例有独立内存空间
线程进程内的执行单元共享进程内存
std::threadC++11 线程库#include <thread>
join等待线程结束阻塞当前线程
detach分离线程后台运行,不再可 join

进程 vs 线程

维度进程线程
内存空间独立共享
通信管道/队列/文件直接共享变量(需同步)
切换开销大小
创建开销大小
崩溃影响只影响自身可能带崩整个进程
适用场景隔离、分布式高并发、I/O 密集

创建线程

#include <iostream>
#include <thread>

void hello() {
    std::cout << "Hello from thread\n";
}

int main() {
    // 方式1: 无参函数
    std::thread t1(hello);

    // 方式2: lambda
    std::thread t2([]{
        std::cout << "Hello from lambda\n";
    });

    // 方式3: 带参函数
    auto print_num = [](int n) {
        std::cout << "Number: " << n << "\n";
    };
    std::thread t3(print_num, 42);

    t1.join();
    t2.join();
    t3.join();
    return 0;
}

join 与 detach

操作行为何时用
join()等待线程结束才继续需要线程结果时
detach()立即返回,线程后台跑结果不关心时(慎用)

陷阱:线程要么 join 要么 detach,都不做 → 析构时 std::terminate() 崩溃。detach 的线程访问已销毁的对象 → 未定义行为。

传参陷阱

#include <thread>

void modify(int& x) { x = 100; }   // 想通过引用改外部变量

int main() {
    int value = 5;

    // std::thread t1(modify, value);           // 编译错误:参数被拷贝,绑不上 int&
    std::thread t2(modify, std::ref(value));    // 正确:显式包引用

    t2.join();
    return 0;
}

std::thread 默认把实参拷贝进线程上下文,所以传引用必须用 std::ref 显式包装;同理,把临时对象或局部对象的指针交给线程,线程还没跑对象就可能已析构。

常见坑

问题原因解决
std::terminate 崩溃线程未 join/detach确认每个线程都 join 或 detach
数据竞争多线程同时写共享变量加锁
悬空引用线程访问已析构对象传值 / std::ref / 智能指针
乱序输出cout 不是线程安全加互斥锁或原子输出

顿悟

  • 线程的默认行为是"不等待":主线程退出,程序结束,残余线程被强杀。join 是"我要等你干完"的显式承诺。
  • 共享内存是把双刃剑:省去进程间通信的麻烦,但引入同步问题,这是并发编程的核心难题。
  • C++11 之后才"官方"支持线程:之前靠 pthread;现在跨平台直接 std::thread。

自测清单

  • 理解进程与线程的区别
  • 能用 std::thread 创建线程(函数 / lambda / 带参)
  • 理解 join 与 detach 的差异,知道不调用会怎样
  • 知道传引用要用 std::ref
  • 意识到共享数据需要同步