前言
在c++中,多线程编程的线程同步和通信主要依赖于锁机制和信号机制。
一、锁机制
锁用于控制对共享资源的访问,防止多个线程同时修改数据导致竞态条件。
- 互斥锁(mutex)
作用:确保同一时间只有一个线程能访问共享资源。
类型:
○ std::mutex:基本互斥锁。
○ std::recursive_mutex:允许同一线程多次加锁(解决递归调用中的锁问题)。
○ std::shared_mutex(c++17):读写锁,允许多个读线程或单个写线程访问。
基本用法
#include <mutex> std::mutex mtx; void thread_func() { mtx.lock(); // 访问共享资源 mtx.unlock(); }
raii包装器(推荐使用,避免手动解锁):
std::lock_guard:自动在作用域内加锁和解锁。
{ std::lock_guard<std::mutex> lock(mtx); // 自动加锁,离开作用域自动解锁 }
std::unique_lock:更灵活,支持延迟加锁和手动控制。
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); lock.lock(); // 手动加锁 // ... lock.unlock(); // 可提前解锁
- 读写锁(shared mutex)
适用场景:读多写少的情况,提高并发性能。
用法:
#include <shared_mutex> std::shared_mutex rw_mtx; // 读操作(共享锁) { std::shared_lock<std::shared_mutex> lock(rw_mtx); // 多个读线程可同时访问 } // 写操作(独占锁) { std::unique_lock<std::shared_mutex> lock(rw_mtx); // 仅一个写线程可访问 }
- 死锁预防
原则:
a. 按固定顺序加锁。
b. 使用std::lock同时加多个锁(避免死锁)。
std::lock(mtx1, mtx2); // 原子性锁定多个互斥量 std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
二、信号机制(条件变量)
- 核心操作
- 等待(wait):线程在条件不满足时挂起。 通知(notify):条件满足时唤醒等待的线程。
notify_one():唤醒一个等待线程。
notify_all():唤醒所有等待线程。
基本用法等待条件(需配合互斥锁和谓词检查):
std::mutex mtx; std::condition_variable cv; bool data_ready = false; // 等待线程 { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, [] { return data_ready; }); // 防止虚假唤醒 // 条件满足后执行操作 } // 通知线程 { std::lock_guard<std::mutex> lock(mtx); data_ready = true; cv.notify_one(); // 或 notify_all() }
- 避免虚假唤醒
使用带有谓词检查的wait:
cv.wait(lock, predicate); // 等价于 while (!predicate) cv.wait(lock);
三、信号量(semaphore)
c++20 引入了std::counting_semaphore,用于控制同时访问资源的线程数量。
- 基本用法
#include <semaphore> std::counting_semaphore<10> sem(3); // 最大计数10,初始3 sem.acquire(); // 计数减1(如果计数为0则阻塞) // 访问资源 sem.release(); // 计数加1
- 适用场景
● 限制并发线程数(如连接池)。
● 生产者-消费者模型中的缓冲区控制。
四、锁与信号机制的对比
到此这篇关于c++锁机制与信号机制的文章就介绍到这了,更多相关c++锁机制与信号机制内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论