【C++】零基础入门 · 第 17 节:多线程编程基础

在现代计算机中,多核 CPU 已经成为标配。多线程编程让你的程序能够同时执行多个任务,充分利用多核性能。C++11 引入了标准的线程库,让多线程编程变得更加简单和安全。

1. 什么是多线程?

1.1 单线程 vs 多线程

单线程:程序只有一个执行流,任务依次执行。

复制代码
任务A → 任务B → 任务C

多线程:程序有多个执行流,任务可以并行执行。

复制代码
线程1:任务A → 任务C
线程2:任务B →

1.2 并发 vs 并行

  • 并发(Concurrency):多个任务交替执行,可能是单核 CPU
  • 并行(Parallelism):多个任务真正同时执行,需要多核 CPU

1.3 为什么需要多线程?

  • 提高性能:充分利用多核 CPU
  • 响应性:后台任务不阻塞 UI
  • 资源利用:等待 I/O 时执行其他任务

2. std::thread 基础

2.1 创建线程

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void hello() {
    cout << "Hello from thread!" << endl;
}

int main() {
    thread t(hello);  // 创建线程
    t.join();  // 等待线程结束
    cout << "Hello from main!" << endl;
    return 0;
}

注意 :必须调用 join()detach(),否则程序会崩溃。

2.2 join vs detach

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void worker() {
    cout << "Worker thread running" << endl;
}

int main() {
    // 方式一:join - 等待线程结束
    thread t1(worker);
    t1.join();  // 主线程等待 t1 结束
    
    // 方式二:detach - 分离线程
    thread t2(worker);
    t2.detach();  // t2 在后台独立运行
    
    // 注意:detach 后不能再 join
    // 主线程结束时,detach 的线程也会被强制结束
    
    this_thread::sleep_for(chrono::milliseconds(100));
    return 0;
}

2.3 传递参数

cpp 复制代码
#include <iostream>
#include <thread>
#include <string>
using namespace std;

void printMessage(const string& msg, int count) {
    for (int i = 0; i < count; i++) {
        cout << msg << " " << i << endl;
    }
}

int main() {
    // 传递参数给线程函数
    thread t(printMessage, "Hello", 3);
    t.join();
    
    return 0;
}

2.4 Lambda 表达式创建线程

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

int main() {
    int x = 42;
    
    // 使用 Lambda 创建线程
    thread t([x]() {
        cout << "x = " << x << endl;
    });
    
    t.join();
    
    return 0;
}

2.5 成员函数作为线程入口

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

class Worker {
public:
    void doWork(int id) {
        cout << "Worker " << id << " is working" << endl;
    }
};

int main() {
    Worker worker;
    
    // 成员函数需要传递对象指针
    thread t(&Worker::doWork, &worker, 1);
    t.join();
    
    return 0;
}

3. 线程管理

3.1 std::thread 的生命周期

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void worker() {
    this_thread::sleep_for(chrono::seconds(1));
    cout << "Worker done" << endl;
}

int main() {
    thread t(worker);
    
    cout << "Thread joinable: " << t.joinable() << endl;  // 输出:1 (true)
    
    t.join();
    
    cout << "Thread joinable: " << t.joinable() << endl;  // 输出:0 (false)
    
    return 0;
}

3.2 RAII 管理线程

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

class ThreadGuard {
private:
    thread& t;
    
public:
    explicit ThreadGuard(thread& t) : t(t) {}
    
    ~ThreadGuard() {
        if (t.joinable()) {
            t.join();  // 析构时自动 join
        }
    }
    
    // 禁止复制
    ThreadGuard(const ThreadGuard&) = delete;
    ThreadGuard& operator=(const ThreadGuard&) = delete;
};

void worker() {
    cout << "Worker thread" << endl;
}

int main() {
    thread t(worker);
    ThreadGuard guard(t);
    
    // 即使发生异常,也能保证线程被 join
    throw runtime_error("Something went wrong");
    
    return 0;
}

3.3 std::jthread(C++20)

C++20 引入了 jthread,自动在析构时 join:

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

void worker() {
    cout << "Worker thread" << endl;
}

int main() {
    jthread t(worker);  // 自动 join
    
    // 不需要手动 join
    return 0;
}

4. 线程同步

4.1 问题:数据竞争

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

int counter = 0;

void increment() {
    for (int i = 0; i < 100000; i++) {
        counter++;  // 非原子操作!
    }
}

int main() {
    thread t1(increment);
    thread t2(increment);
    
    t1.join();
    t2.join();
    
    // 结果不确定,可能小于 200000
    cout << "Counter: " << counter << endl;
    
    return 0;
}

4.2 std::mutex(互斥锁)

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

int counter = 0;
mutex mtx;

void increment() {
    for (int i = 0; i < 100000; i++) {
        lock_guard<mutex> lock(mtx);  // 自动加锁和解锁
        counter++;
    }
}

int main() {
    thread t1(increment);
    thread t2(increment);
    
    t1.join();
    t2.join();
    
    cout << "Counter: " << counter << endl;  // 输出:200000
    
    return 0;
}

4.3 lock_guard vs unique_lock

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

mutex mtx;

// lock_guard:简单场景
void simpleLock() {
    lock_guard<mutex> lock(mtx);
    // 临界区
}  // 自动解锁

// unique_lock:需要更灵活的控制
void flexibleLock() {
    unique_lock<mutex> lock(mtx);
    // 临界区
    
    lock.unlock();  // 手动解锁
    
    // 做一些不需要锁的操作
    
    lock.lock();  // 重新加锁
    // 临界区
}

4.4 死锁

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

mutex mtx1, mtx2;

void thread1() {
    lock_guard<mutex> lock1(mtx1);
    this_thread::sleep_for(chrono::milliseconds(10));
    lock_guard<mutex> lock2(mtx2);  // 等待 mtx2
    cout << "Thread 1 done" << endl;
}

void thread2() {
    lock_guard<mutex> lock2(mtx2);
    this_thread::sleep_for(chrono::milliseconds(10));
    lock_guard<mutex> lock1(mtx1);  // 等待 mtx1
    cout << "Thread 2 done" << endl;
}

int main() {
    thread t1(thread1);
    thread t2(thread2);
    
    t1.join();  // 死锁!程序卡住
    t2.join();
    
    return 0;
}

解决方案

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

mutex mtx1, mtx2;

void thread1() {
    // 同时锁定两个互斥锁
    lock(mtx1, mtx2);
    lock_guard<mutex> lock1(mtx1, adopt_lock);
    lock_guard<mutex> lock2(mtx2, adopt_lock);
    cout << "Thread 1 done" << endl;
}

void thread2() {
    // 使用相同的顺序锁定
    lock(mtx1, mtx2);
    lock_guard<mutex> lock1(mtx1, adopt_lock);
    lock_guard<mutex> lock2(mtx2, adopt_lock);
    cout << "Thread 2 done" << endl;
}

int main() {
    thread t1(thread1);
    thread t2(thread2);
    
    t1.join();
    t2.join();
    
    return 0;
}

5. 条件变量

5.1 基本用法

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

queue<int> dataQueue;
mutex mtx;
condition_variable cv;
bool finished = false;

void producer() {
    for (int i = 0; i < 10; i++) {
        {
            lock_guard<mutex> lock(mtx);
            dataQueue.push(i);
            cout << "Produced: " << i << endl;
        }
        cv.notify_one();  // 通知消费者
        this_thread::sleep_for(chrono::milliseconds(100));
    }
    
    {
        lock_guard<mutex> lock(mtx);
        finished = true;
    }
    cv.notify_all();
}

void consumer() {
    while (true) {
        unique_lock<mutex> lock(mtx);
        cv.wait(lock, []() { return !dataQueue.empty() || finished; });
        
        while (!dataQueue.empty()) {
            int data = dataQueue.front();
            dataQueue.pop();
            cout << "Consumed: " << data << endl;
        }
        
        if (finished && dataQueue.empty()) {
            break;
        }
    }
}

int main() {
    thread prod(producer);
    thread cons(consumer);
    
    prod.join();
    cons.join();
    
    return 0;
}

6. 原子操作

6.1 std::atomic

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 100000; i++) {
        counter++;  // 原子操作
    }
}

int main() {
    thread t1(increment);
    thread t2(increment);
    
    t1.join();
    t2.join();
    
    cout << "Counter: " << counter << endl;  // 输出:200000
    
    return 0;
}

6.2 常用原子操作

cpp 复制代码
#include <iostream>
#include <atomic>
using namespace std;

int main() {
    atomic<int> val(0);
    
    val.store(10);           // 存储
    int x = val.load();      // 加载
    val++;                   // 自增
    val--;                   // 自减
    val += 5;                // 加法
    val.compare_exchange_strong(x, 20);  // CAS 操作
    
    cout << val << endl;     // 输出:15
    
    return 0;
}

7. 线程局部存储

7.1 thread_local

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

thread_local int localVar = 0;

void worker(int id) {
    localVar = id;
    this_thread::sleep_for(chrono::milliseconds(100));
    cout << "Thread " << id << ": localVar = " << localVar << endl;
}

int main() {
    thread t1(worker, 1);
    thread t2(worker, 2);
    
    t1.join();
    t2.join();
    
    // 每个线程有自己的 localVar 副本
    return 0;
}

8. 异步操作

8.1 std::async 和 std::future

cpp 复制代码
#include <iostream>
#include <future>
#include <chrono>
using namespace std;

int compute() {
    this_thread::sleep_for(chrono::seconds(1));
    return 42;
}

int main() {
    // 异步执行
    future<int> result = async(launch::async, compute);
    
    cout << "Doing other work..." << endl;
    
    // 获取结果(会阻塞直到完成)
    int value = result.get();
    cout << "Result: " << value << endl;
    
    return 0;
}

8.2 packaged_task

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>
using namespace std;

int multiply(int a, int b) {
    return a * b;
}

int main() {
    packaged_task<int(int, int)> task(multiply);
    future<int> result = task.get_future();
    
    thread t(move(task), 6, 7);
    
    cout << "Result: " << result.get() << endl;  // 输出:42
    
    t.join();
    
    return 0;
}

9. 实际应用

9.1 线程池

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
using namespace std;

class ThreadPool {
private:
    vector<thread> workers;
    queue<function<void()>> tasks;
    mutex mtx;
    condition_variable cv;
    bool stop;
    
public:
    ThreadPool(int numThreads) : stop(false) {
        for (int i = 0; i < numThreads; i++) {
            workers.emplace_back([this]() {
                while (true) {
                    function<void()> task;
                    {
                        unique_lock<mutex> lock(mtx);
                        cv.wait(lock, [this]() { return stop || !tasks.empty(); });
                        
                        if (stop && tasks.empty()) return;
                        
                        task = move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            lock_guard<mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (auto& worker : workers) {
            worker.join();
        }
    }
    
    template<class F>
    void enqueue(F&& f) {
        {
            lock_guard<mutex> lock(mtx);
            tasks.emplace(forward<F>(f));
        }
        cv.notify_one();
    }
};

int main() {
    ThreadPool pool(4);
    
    for (int i = 0; i < 8; i++) {
        pool.enqueue([i]() {
            cout << "Task " << i << " running in thread " 
                 << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        });
    }
    
    this_thread::sleep_for(chrono::seconds(1));
    
    return 0;
}

10. 常见陷阱

10.1 忘记 join 或 detach

cpp 复制代码
// 错误
thread t(worker);
// 程序结束时会崩溃

// 正确
thread t(worker);
t.join();  // 或 t.detach()

10.2 传递引用参数

cpp 复制代码
void modify(int& x) {
    x = 100;
}

int main() {
    int val = 42;
    // thread t(modify, val);  // 错误!
    thread t(modify, ref(val));  // 使用 ref
    t.join();
    cout << val << endl;  // 输出:100
}

10.3 数据竞争

始终确保共享数据的访问是同步的。

11. 总结

C++ 多线程编程的核心概念:

  • std::thread:创建和管理线程
  • std::mutex:互斥锁,保护共享数据
  • std::condition_variable:线程间通信
  • std::atomic:原子操作,无锁编程
  • std::async:异步任务

最佳实践

  • 使用 RAII 管理线程生命周期
  • 优先使用 lock_guardunique_lock
  • 避免死锁:固定加锁顺序
  • 尽量使用原子操作代替互斥锁
  • C++20 使用 jthread 简化线程管理

下一节我们将学习智能指针与多线程的结合使用

相关推荐
noipp1 小时前
推荐题目:洛谷 P1115 最大子段和
算法
tkevinjd1 小时前
事务、ACID与隔离
java·数据库·sql
阿洛学长1 小时前
Kali Linux 虚拟机安装(VMware Workstation 17)
java·linux·服务器
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第91题】【Mysql篇】第21题:分布式锁的使用场景和原理?
java·数据库·分布式·mysql·面试
JAVA社区1 小时前
Java高级全套教程(十三)—— 分布式锁超详细实战详解(原理+三种方案企业级落地)
java·开发语言·分布式·spring cloud·面试·java-zookeeper
Mahir081 小时前
MyBatis 延迟加载深度解密:从使用方式到底层动态代理原理全解
java·后端·面试·mybatis
超梦dasgg1 小时前
Java 生产环境 Maven 实战指南
java·开发语言·maven
A_humble_scholar1 小时前
C++11 学习笔记:统一初始化、右值引用与完美转发
c++·笔记·学习
贺国亚1 小时前
Agent 工程实践 · 生产落地 Playbook
java·人工智能·aigc