C++: 多线程基础

最后更新:2026-08-26

第42课我们学了正则表达式。

现在,我们要进入C++的高级领域——多线程编程

现代计算机都是多核的,单线程程序只能用到一个核,太浪费了。

多线程让你同时做好几件事,大幅提升性能。


1. 多线程概述

(1) 1.1 什么是线程?

线程(Thread)是程序执行的最小单位

进程 vs 线程:

生活类比:


(2) 1.2 为什么要用多线程?

优势 说明
提高性能 多核并行计算
提高响应 UI线程不阻塞
简化设计 把不同任务分给不同线程


2. 创建线程

(1) 2.1 基本用法

C++11在 thread 头文件中提供了 std::thread 类。

示例:创建线程(难度⭐)

▶ 示例 1:多线程编程演示(难度⭐)

CPP
#include <iostream>
#include <thread>

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

int main() {
 std::thread t(hello); // 创建线程
 t.join(); // 等待线程结束
 
 std::cout << "Main thread ends" << std::endl;
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
主线程:开始
主线程:等待子线程完成
子线程:Hello from thread
主线程:结束

运行结果:

TEXT 📖 仅展示
Hello from thread!
Main thread ends

(2) 2.2 join vs detach

函数 功能 说明
join() 等待线程结束 阻塞当前线程
detach() 分离线程 线程独立运行,无法再join

示例:用join等待(难度⭐)

CPP
#include <iostream>
### ▶ 示例 2:多线程编程演示(难度⭐)

#include <thread>
#include <chrono>

void worker(int id) {
 for (int i = 0; i < 3; i++) {
 std::cout << "Worker " << id << " working..." << std::endl;
 std::this_thread::sleep_for(std::chrono::milliseconds(500));
 }
}

int main() {
 std::thread t1(worker, 1);
 std::thread t2(worker, 2);
 
 t1.join(); // 等待t1结束
 t2.join(); // 等待t2结束
 
 std::cout << "All workers done" << std::endl;
 return 0;
}


3. 线程参数传递

(1) 3.1 传递参数

std::thread 构造函数可以接受任意可调用对象参数

示例:传递参数(难度⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <string>

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

int main() {
 std::thread t(printMessage, "Hello", 3);
 t.join();
 
 return 0;
}

(2) 3.2 引用传递

默认情况下,参数按值传递。要用引用,必须用 std::ref

示例:用引用传递(难度⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <functional>

void increment(int& x) {
 x++;
}

int main() {
 int counter = 0;
 std::thread t(increment, std::ref(counter));
 t.join();
 
 std::cout << "Counter: " << counter << std::endl; // 输出:1
 return 0;
}


4. 互斥量

(1) 4.1 为什么需要互斥量?

问题: 多个线程同时访问共享数据,会导致数据竞争(Data Race)。

示例:数据竞争(难度⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <vector>

int counter = 0;

void increment() {
 for (int i = 0; i < 1000; i++) {
 counter++; // 多个线程同时修改,结果不确定
 }
}

int main() {
 std::vectorstd::thread threads;
 for (int i = 0; i < 10; i++) {
 threads.emplace_back(increment);
 }
 
 for (auto& t : threads) {
 t.join();
 }
 
 std::cout << "Counter: " << counter << std::endl; // 期望10000,实际可能少于
 return 0;
}

(2) 4.2 用互斥量保护共享数据

互斥量(Mutex)用于保证同一时间只有一个线程访问共享数据

示例:用mutex保护(难度⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>

int counter = 0;
std::mutex mtx;

void increment() {
 for (int i = 0; i < 1000; i++) {
 mtx.lock(); // 加锁
 counter++;
 mtx.unlock(); // 解锁
 }
}

int main() {
 std::vectorstd::thread threads;
 for (int i = 0; i < 10; i++) {
 threads.emplace_back(increment);
 }
 
 for (auto& t : threads) {
 t.join();
 }
 
 std::cout << "Counter: " << counter << std::endl; // 一定是10000
 return 0;
}

(3) 4.3 lock_guard——RAII方式

推荐用法:std::lock_guard 自动加锁/解锁。

CPP
void increment() {
 for (int i = 0; i < 1000; i++) {
 std::lock_guardstd::mutex lock(mtx); // 构造时加锁,析构时解锁
 counter++;
 } // 自动解锁
}


5. 条件变量

(1) 5.1 为什么需要条件变量?

问题: 线程需要等待某个条件成立(如队列非空)。

解决方案: std::condition_variable


(2) 5.2 示例:生产者-消费者(难度⭐⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

std::queueint q;
std::mutex mtx;
std::condition_variable cv;

void producer() {
 for (int i = 0; i < 10; i++) {
 std::lock_guardstd::mutex lock(mtx);
 q.push(i);
 std::cout << "生产:" << i << std::endl;
 cv.notify_one(); // 通知消费者
 }
}

void consumer() {
 for (int i = 0; i < 10; i++) {
 std::unique_lockstd::mutex lock(mtx);
 cv.wait(lock, { return !q.empty(); }); // 等待队列非空
 
 int value = q.front();
 q.pop();
 std::cout << "消费:" << value << std::endl;
 }
}

int main() {
 std::thread t1(producer);
 std::thread t2(consumer);
 
 t1.join();
 t2.join();
 
 return 0;
}

输出:

TEXT 📖 仅展示
生产:
消费:


6. 异步任务

(1) 6.1 std::async

std::async 用于启动异步任务,返回 std::future

示例:异步计算(难度⭐⭐)

CPP
#include <iostream>
#include <future>

int calculate(int x) {
 return x * x;
}

int main() {
 std::futureint result = std::async(calculate, 10);
 std::cout << "结果:" << result.get() << std::endl; // 输出:100
 
 return 0;
}

❓ 常见问题

Q:多少线程合适? A:通常等于CPU核心数。太多会导致上下文切换开销。


Q:死锁是什么? A:两个线程互相等待对方释放锁,导致谁也无法继续。

避免方法:


Q std::thread和OpenMP哪个好?
A - std::thread:灵活,跨平台 - OpenMP:简单,适合科学计算

▶ 示例 3:创建线程(难度⭐)

CPP
#include <iostream>
#include <thread>

void printNumbers(int start, int end) {
    for (int i = start; i <= end; i++) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::thread t1(printNumbers, 1, 5);
    std::thread t2(printNumbers, 10, 15);

    t1.join();
    t2.join();

    return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
 

输出(示例):

TEXT 📖 仅展示
1 10 2 11 3 12 4 13 5 14 15
💡 提示std::thread 创建线程,join() 等待线程结束。输出顺序可能因线程调度而不同。


知识点 要点
std::thread 创建线程
join/detach 等待/分离线程
std::mutex 互斥量,保护共享数据
std::lock_guard RAII方式加锁
std::condition_variable 条件变量,线程间通信
std::async 异步任务

📖 小节

📝 作业

  1. **基础题 (Difficulty ⭐):创建两个线程,分别输出"线程A"和"线程B",观察输出顺序的随机性。

  2. **进阶题 (Difficulty ⭐⭐):创建 4 个线程,每个线程计算一段数值的累加(如 1-2500、2501-5000...),最后汇总累加结果。

  3. **挑战题 (Difficulty ⭐⭐⭐):用 std::async 和 std::future 实现一个并发下载模拟器:创建 3 个异步任务,每个模拟下载不同大小的文件,等待全部完成后汇总。



下一课:多线程同步(#44)

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏