您现在的位置是:首页 >其他 >C++ 多线程 手段总结网站首页其他
C++ 多线程 手段总结
简介C++ 多线程 手段总结
C++ 多线程手段详解
1. std::mutex
使用场景
std::mutex
用于保护共享资源,确保在同一时刻只有一个线程可以访问该资源。常见的使用场景包括:
- 保护全局变量
- 保护文件读写操作
- 保护数据库连接
实现原理
std::mutex
的实现依赖于操作系统的同步原语。在 Windows 上,它调用 CRITICAL_SECTION
,而在 Linux 上,它调用 pthread_mutex_t
。
例子
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 互斥锁
void printThread(int id) {
std::lock_guard<std::mutex> lock(mtx); // 自动管理锁
std::cout << "Thread ID: " << id << std::endl;
}
int main() {
std::thread t1(printThread, 1);
std::thread t2(printThread, 2);
t1.join();
t2.join();
return 0;
}
2. std::condition_variable
使用场景
std::condition_variable
用于线程间的通信和协调,特别是在生产者-消费者模式中非常有用。
实现原理
std::condition_variable
本质上是比 std::mutex
多了阻塞当前线程的功能,例如等待和唤醒操作。具体实现如下:
wait
方法:- 释放锁:调用
wait
方法时,首先会释放当前持有的锁(通常是std::unique_lock
)。这是通过调用std::unique_lock
的unlock
方法实现的。 - 进入等待状态:然后,线程会进入等待状态,释放 CPU 资源。这是通过调用操作系统级别的条件变量的等待函数实现的,例如
pthread_cond_wait
或WaitForMultipleObjects
。 - 重新获取锁:当线程被唤醒时,它会重新尝试获取之前释放的锁。这是通过调用
std::unique_lock
的构造函数或lock
方法实现的。一旦获取成功,才会从wait
方法返回。
- 释放锁:调用
notify_one
方法:- 唤醒一个线程:调用
notify_one
方法时,会选择一个等待在条件变量上的线程并将其唤醒。具体选择哪个线程是由操作系统调度器决定的。这是通过调用操作系统级别的条件变量的通知函数实现的,例如pthread_cond_signal
或PulseEvent
。
- 唤醒一个线程:调用
notify_all
方法:- 唤醒所有线程:调用
notify_all
方法时,会唤醒所有等待在条件变量上的线程。这些线程都会尝试重新获取锁,但只有一个线程能成功获取锁并继续执行,其他线程会再次进入等待状态。这是通过调用操作系统级别的条件变量的通知函数实现的,例如pthread_cond_broadcast
或PulseEvent
。
- 唤醒所有线程:调用
例子
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::condition_variable cv;
std::mutex mtx;
bool ready = false;
void waitFunction() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待条件满足
std::cout << "Condition met, continuing..." << std::endl;
}
void notifyFunction() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟延迟
std::lock_guard<std::mutex> lock(mtx);
ready = true;
cv.notify_one(); // 通知等待的线程
}
int main() {
std::thread t1(waitFunction);
std::thread t2(notifyFunction);
t1.join();
t2.join();
return 0;
}
3. std::atomic
使用场景
std::atomic
主要用于简单资源的同步,适用于以下场景:
- 计数器:确保计数操作是原子的。
- 标志位:确保标志位的读写操作是原子的。
- 无锁数据结构:实现高性能的无锁数据结构。
- 并发计数器:确保多个计数器的读写操作是原子的。
- 状态同步:确保状态的读写操作是原子的,避免竞态条件。
实现原理
std::atomic
的实现基于硬件支持和内存模型:
- 硬件支持:
std::atomic
会被编译器编译成原子化操作的 CPU 指令。 - 内存模型:能够使用不同的内存模型,提供更精细化的可见性。常见的内存顺序选项包括:
std::memory_order_relaxed
:最弱的内存顺序,只保证操作本身是原子的,不提供任何额外的内存顺序保证。std::memory_order_consume
:保证消费操作之前的读取操作在消费操作之后可见。std::memory_order_acquire
:保证读取操作之前的读取操作在读取操作之后可见。std::memory_order_release
:保证写入操作之前的写入操作在写入操作之后可见。std::memory_order_acq_rel
:同时具有acquire
和release
的效果。std::memory_order_seq_cst
:最强的内存顺序,保证所有线程看到的操作顺序是一致的。
例子
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> counter(0);
void incrementCounter() {
for (int i = 0; i < 100000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
std::thread t1(incrementCounter);
std::thread t2(incrementCounter);
t1.join();
t2.join();
std::cout << "Final counter value: " << counter.load() << std::endl;
return 0;
}
4. std::async
和 std::future
使用场景
std::async
和 std::future
适用于以下场景:
- 异步计算:需要异步执行计算任务的场景。
- 异步 I/O:需要非阻塞 I/O 的场景。
- 异步任务链:需要按顺序执行多个异步任务的场景。
实现原理
std::async
和 std::future
基于 SharedState
共享状态来实现,主要通过 std::mutex
和 std::condition_variable
进行同步。
例子
#include <iostream>
#include <future>
#include <thread>
int computeValue(int x) {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟延迟
return x * x;
}
int main() {
std::future<int> result = std::async(std::launch::async, computeValue, 5);
std::cout << "Doing some other work..." << std::endl;
int value = result.get(); // 获取异步任务的结果
std::cout << "Result: " << value << std::endl;
return 0;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。