C++多线程高级技巧
发布时间: 2024-12-10 02:22:25 阅读量: 3 订阅数: 12
C++多线程编程实践指南:从基础到高级应用
![C++多线程高级技巧](https://i1.wp.com/yellowcodebooks.com/wp-content/uploads/2019/07/ThreadPoolExecutor.png?ssl=1)
# 1. C++多线程编程概述
## 1.1 C++多线程编程的重要性
C++多线程编程是现代软件开发中的一个重要领域,尤其在多核处理器广泛普及的今天。通过创建多个执行线程,可以充分利用CPU资源,提高程序的执行效率,实现复杂任务的并行处理。掌握C++多线程编程,不仅能提升软件性能,还能增强程序对异步事件的响应能力。
## 1.2 C++11与之前的多线程编程
C++11标准之前,C++多线程编程主要依赖于第三方库或操作系统API,如POSIX线程(pthread)库。自C++11开始,C++标准库正式引入了线程支持,提供了诸如`<thread>`, `<mutex>`, `<condition_variable>`等头文件,大大简化了线程的创建和管理过程,使多线程编程更加安全和易于实现。
## 1.3 C++11引入的多线程API
C++11引入的线程库提供了一系列用户友好的API,包括线程类`std::thread`,用于线程同步的互斥锁`std::mutex`、条件变量`std::condition_variable`等。这些API的设计考虑到了现代多线程环境的复杂性,不仅提供了基础的同步机制,还支持更高级的并行算法和并发数据结构,为开发者构建高效多线程应用提供了强大的工具集。
```cpp
#include <iostream>
#include <thread>
#include <chrono>
void hello() {
std::cout << "Hello from the new thread!\n";
}
int main() {
std::thread t(hello);
t.join();
std::cout << "Hello from the main thread!\n";
return 0;
}
```
在上述示例中,使用`std::thread`创建了一个新线程,执行`hello()`函数,同时主线程继续执行输出。通过调用`join()`确保主线程等待子线程完成工作后再退出。
# 2. 线程同步与通信
## 2.1 线程同步机制
### 2.1.1 互斥锁(Mutex)
互斥锁是一种最基本的线程同步机制,用于保证在任何时刻只有一个线程可以访问共享资源。在C++中,可以通过`std::mutex`来使用互斥锁。互斥锁的一个典型使用场景是在修改共享数据时避免数据竞争。
```cpp
#include <mutex>
#include <thread>
std::mutex mtx; // 创建互斥锁对象
void func(int n) {
for (int i = 0; i < 5; ++i) {
mtx.lock(); // 锁定互斥锁
// 执行相关操作,修改共享数据
mtx.unlock(); // 解锁互斥锁
}
}
int main() {
std::thread t1(func, 1);
std::thread t2(func, 2);
t1.join();
t2.join();
return 0;
}
```
在上述代码中,两个线程`func`函数中的共享资源被互斥锁保护。每次只有一个线程可以执行锁定后的代码块,直到它调用`unlock()`释放锁。这样可以保证在数据修改时不会发生冲突。
互斥锁的使用要注意避免死锁(当两个或多个线程相互等待对方释放锁时,就会发生死锁),通常需要确保锁的获取和释放顺序一致。
### 2.1.2 条件变量(Condition Variables)
条件变量通常用于线程间通信,允许线程阻塞等待直到某个条件为真。在C++中,条件变量通过`std::condition_variable`来使用。条件变量与互斥锁配合使用,以确保条件检查和线程阻塞的原子性。
```cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) {
cv.wait(lck); // 等待条件变量
}
// 当条件变量通知后,打印线程ID
std::cout << "Thread " << id << '\n';
}
void go() {
std::unique_lock<std::mutex> lck(mtx);
ready = true;
cv.notify_all(); // 通知所有等待的线程
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(print_id, i);
}
std::cout << "10 threads ready to race...\n";
go(); // 开始所有线程
for (auto& th : threads) {
th.join();
}
return 0;
}
```
在此示例中,主线程通过`go()`函数通知所有工作线程它们可以继续执行。工作线程在`print_id()`函数中阻塞等待,直到条件变量`ready`被设置为`true`。
条件变量非常适合于生产者-消费者模型,其中生产者会通知消费者有新的数据可用。
## 2.2 线程间通信
### 2.2.1 信号量(Semaphores)
信号量是一种通用的同步机制,可以用来控制对共享资源的访问数量。在C++中,`std::counting_semaphore`提供了信号量的实现。信号量维护一个内部计数器,可以初始化为任意的值,并且在每个`wait()`调用时递减,在每个`signal()`调用时递增。
```cpp
#include <semaphore>
#include <chrono>
#include <thread>
std::counting_semaphore<3> sem{0}; // 信号量初始为0,限制最多3个线程同时进入临界区
void task(int i) {
sem.acquire(); // 等待信号量
std::cout << "Thread " << i << " is in critical section.\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
sem.release(); // 释放信号量
}
int main() {
std::thread t[10];
for (int i = 0; i < 10; ++i) {
t[i] = std::thread(task, i);
}
for (auto& th : t) {
th.join();
}
return 0;
}
```
这个例子中,信号量初始化为0,并被设置为最多允许3个线程同时执行。每当线程进入临界区时,它调用`sem.acquire()`,如果信号量的值已经是0,则线程将被阻塞,直到信号量被`sem.release()`增加。当线程退出临界区时,调用`sem.release()`释放信号量,允许其他等待的线程进入。
### 2.2.2 事件(Events)
事件机制允许线程等待某个信号,并在信号发生时继续执行。在C++中,`std::co
0
0