C++ 多线程锁处理
最编程
2024-10-01 07:42:51
...
一、基本介绍
在C++中,多线程编程同样需要处理线程安全问题,C++11及更高版本提供了一套标准库来支持多线程编程,包括锁的处理。
二、常见锁处理方式
互斥锁(std::mutex):
-
std::mutex
是最基本的锁类型,提供了互斥访问共享资源的能力。 - 它不允许一个线程多次获得锁(即不可重入)。
#include <mutex>
#include <thread>
std::mutex mtx;
int shared_data = 0;
void increment() {
mtx.lock();
shared_data++;
mtx.unlock();
}
递归互斥锁(std::recursive_mutex):
-
std::recursive_mutex
允许同一个线程多次获得锁,这在递归函数调用中非常有用。
#include <mutex>
#include <thread>
std::recursive_mutex mtx;
int shared_data = 0;
void increment() {
mtx.lock();
shared_data++;
// 可以再次调用 increment() 而不会死锁
increment();
mtx.unlock();
}
读写锁(std::shared_mutex):
- C++17引入了
std::shared_mutex
,它允许多个线程同时读取共享资源,但写入时需要独占访问。
#include <shared_mutex>
#include <thread>
std::shared_mutex rw_mutex;
int shared_data = 0;
void read() {
std::shared_lock<std::shared_mutex> lock(rw_mutex);
// 读取 shared_data
}
void write(int value) {
std::unique_lock<std::shared_mutex> lock(rw_mutex);
shared_data = value;
}
条件变量(std::condition_variable):
-
std::condition_variable
用于线程间的同步,允许一个线程等待某个条件成立。
#include <mutex>
#include <condition_variable>
#include <thread>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker_thread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
// 执行工作
}
void main_thread() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
原子操作(std::atomic):
- 对于简单的数据类型,可以使用原子类型
std::atomic
来保证操作的原子性,而不需要使用锁。
#include <atomic>
#include <thread>
std::atomic<int> shared_data(0);
void increment() {
shared_data++;
}
锁的封装(std::lock_guard 和 std::unique_lock):
-
std::lock_guard
和std::unique_lock
是RAII(资源获取即初始化)风格的锁封装,它们在构造时自动获取锁,在析构时自动释放锁。
#include <mutex>
#include <thread>
std::mutex mtx;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
shared_data++;
}
void long_task() {
std::unique_lock<std::mutex> lock(mtx);
// 执行一些任务
lock.unlock();
// 执行不需要锁的任务
}
一次性初始化(std::once_flag 和 std::call_once):
-
std::once_flag
和std::call_once
用于确保某个函数或代码块只被执行一次,即使在多线程环境中。
#include <mutex>
std::once_flag once_flag;
void (*init_func)() = nullptr;
void init() {
// 初始化代码
}
void do_init() {
std::call_once(once_flag, init);
}
三、注意事项
- 避免死锁:确保所有线程获取锁的顺序一致,或者使用
try_lock
来尝试获取锁。 - 避免活锁:确保线程在等待锁时能够响应其他线程释放锁的信号。
- 锁的粒度:尽量减小锁的范围,以提高性能。
- 锁的持有时间:尽量减少锁持有的时间,以减少等待时间。