2.1. 线程基础

2.1.1. std::thread 基本用法

#include <thread>
#include <iostream>

// 方式1:函数指针
void worker(int id) {
    std::cout << "Worker " << id << " running\n";
}

// 方式2:Lambda
auto lambda_worker = [](int id) {
    std::cout << "Lambda worker " << id << "\n";
};

// 方式3:函数对象
struct Functor {
    void operator()(int id) const {
        std::cout << "Functor worker " << id << "\n";
    }
};

int main() {
    std::thread t1(worker, 1);
    std::thread t2(lambda_worker, 2);
    std::thread t3(Functor{}, 3);
    
    t1.join();  // 等待线程结束
    t2.join();
    t3.join();
}

2.1.2. 线程的生命周期

2.1.2.1. join vs detach

void example() {
    std::thread t([]{ /* work */ });
    
    // 必须选择一种方式处理线程
    // 方式1:等待线程结束
    t.join();
    
    // 方式2:分离线程(后台运行)
    // t.detach();
    
    // 如果都不做,析构时会调用 std::terminate()!
}

2.1.2.2. RAII 线程管理

// 确保线程被正确 join
class jthread_guard {
    std::thread& t_;
public:
    explicit jthread_guard(std::thread& t) : t_(t) {}
    ~jthread_guard() {
        if (t_.joinable()) {
            t_.join();
        }
    }
    // 禁止拷贝
    jthread_guard(const jthread_guard&) = delete;
    jthread_guard& operator=(const jthread_guard&) = delete;
};

// C++20: std::jthread 自动 join
#include <stop_token>

void worker(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        // 工作
    }
}

void example() {
    std::jthread t(worker);  // 析构时自动 join
    // t.request_stop();     // 请求停止
}  // 自动调用 request_stop() 和 join()

2.1.3. 参数传递

2.1.3.1. 值传递 vs 引用传递

void work_by_value(int x) { x++; }
void work_by_ref(int& x) { x++; }

int main() {
    int value = 0;
    
    // 默认值传递(拷贝)
    std::thread t1(work_by_value, value);
    t1.join();
    std::cout << value;  // 0,未改变
    
    // 引用传递需要 std::ref
    std::thread t2(work_by_ref, std::ref(value));
    t2.join();
    std::cout << value;  // 1,已改变
    
    // 危险:引用已销毁的对象
    // std::thread t([&value]{ value++; });
    // t.detach();  // 危险!value 可能已销毁
}

2.1.3.2. 移动语义

void process(std::unique_ptr<int> ptr) {
    std::cout << *ptr << "\n";
}

int main() {
    auto ptr = std::make_unique<int>(42);
    
    // std::thread t(process, ptr);  // 错误!unique_ptr 不能拷贝
    std::thread t(process, std::move(ptr));  // OK
    t.join();
}

2.1.4. 线程局部存储

// 每个线程有独立的副本
thread_local int tls_counter = 0;

void worker() {
    ++tls_counter;
    std::cout << "TLS counter: " << tls_counter << "\n";
}

int main() {
    std::thread t1(worker);  // 输出 1
    std::thread t2(worker);  // 输出 1(独立的副本)
    t1.join();
    t2.join();
}

2.1.5. 获取线程信息

#include <thread>
#include <iostream>

int main() {
    // 当前线程 ID
    std::cout << "Main thread ID: " << std::this_thread::get_id() << "\n";
    
    // 硬件并发数
    unsigned int n = std::thread::hardware_concurrency();
    std::cout << "Hardware concurrency: " << n << "\n";
    
    std::thread t([] {
        std::cout << "Worker thread ID: " << std::this_thread::get_id() << "\n";
    });
    
    // 获取线程句柄
    auto handle = t.native_handle();
    
    t.join();
}

2.1.6. 线程休眠

#include <thread>
#include <chrono>

void worker() {
    using namespace std::chrono_literals;  // C++14
    
    std::this_thread::sleep_for(100ms);     // 休眠 100 毫秒
    std::this_thread::sleep_for(1s);        // 休眠 1 秒
    
    auto wake_time = std::chrono::steady_clock::now() + 500ms;
    std::this_thread::sleep_until(wake_time);  // 休眠到指定时间点
    
    std::this_thread::yield();  // 让出 CPU 时间片
}

2.1.7. 最佳实践

2.1.7.1. 1. 避免在构造函数中启动线程

class BadExample {
    std::thread t_;
public:
    BadExample() : t_(&BadExample::worker, this) {
        // 危险!对象尚未完全构造
    }
    void worker() { /* 访问成员 */ }
};

class GoodExample {
    std::thread t_;
public:
    void start() {
        t_ = std::thread(&GoodExample::worker, this);
    }
    void worker() { /* 安全 */ }
    ~GoodExample() {
        if (t_.joinable()) t_.join();
    }
};

2.1.7.2. 2. 线程安全地传递数据

// 使用 promise/future 传递结果
#include <future>

int compute() { return 42; }

int main() {
    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    
    std::thread t([&promise] {
        promise.set_value(compute());
    });
    
    int result = future.get();  // 阻塞等待结果
    t.join();
}

2.1.7.3. 3. 合理的线程数量

unsigned int num_threads = std::thread::hardware_concurrency();
if (num_threads == 0) num_threads = 2;  // 默认值

// 对于 CPU 密集型任务
// 线程数 ≈ CPU 核心数

// 对于 IO 密集型任务
// 线程数可以更多,取决于 IO 等待时间比例

警告

不要创建过多线程!线程创建和上下文切换都有开销。 对于大量短任务,考虑使用线程池。