安全使用C++互斥锁的关键是遵循RAII原则,优先使用std::lock_guard或std::unique_lock管理std::mutex,避免手动调用lock()和unlock(),以防异常导致的死锁;对于多锁场景,应使用std::scoped_lock或std::lock确保加锁顺序一致,防止死锁;同时可根据读写频率选择std::shared_mutex,或用std::atomic实现无锁原子操作,结合条件变量、异步任务等机制满足不同同步需求。

在C++多线程编程中,要安全地使用互斥锁,核心在于利用RAII(资源获取即初始化)原则,通过
std::lock_guard
std::unique_lock
std::mutex
安全使用C++互斥锁的关键在于理解并正确运用C++标准库提供的同步原语。最基础的互斥锁是
std::mutex
lock()
unlock()
我们通常会配合
std::lock_guard
std::unique_lock
std::mutex
1. std::lock_guard
立即学习“C++免费学习笔记(深入)”;
std::lock_guard
#include <iostream>
#include <vector>
#include <string>
#include <mutex>
#include <thread>
#include <chrono> // For std::this_thread::sleep_for
std::vector<int> shared_data;
std::mutex mtx; // 全局或成员互斥锁
void add_to_shared_data(int value) {
// 构造时加锁
std::lock_guard<std::mutex> lock(mtx);
// 临界区开始
shared_data.push_back(value);
std::cout << "Thread " << std::this_thread::get_id() << " added: " << value << std::endl;
// 临界区结束,lock_guard析构时自动解锁
}
// int main() {
// std::vector<std::thread> threads;
// for (int i = 0; i < 5; ++i) {
// threads.emplace_back(add_to_shared_data, i);
// }
// for (auto& t : threads) {
// t.join();
// }
// // 验证数据
// std::cout << "Shared data size: " << shared_data.size() << std::endl;
// return 0;
// }2. std::unique_lock
std::unique_lock
std::lock_guard
lock()
try_lock()
try_lock_for()
try_lock_until()
std::unique_lock
unique_lock
这些特性在处理复杂并发场景,比如需要条件变量(
std::condition_variable
// 配合条件变量的示例
std::queue<int> q;
std::mutex q_mtx;
std::condition_variable cv;
bool data_ready = false;
void producer() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟生产时间
{
std::unique_lock<std::mutex> lock(q_mtx); // 构造时加锁
q.push(42);
data_ready = true;
std::cout << "Producer produced 42." << std::endl;
} // lock析构时解锁
cv.notify_one(); // 通知一个等待线程
}
void consumer() {
std::unique_lock<std::mutex> lock(q_mtx); // 构造时加锁
// 等待条件变量,期间会自动解锁,当被唤醒且条件满足时重新加锁
cv.wait(lock, []{ return data_ready; });
int value = q.front();
q.pop();
std::cout << "Consumer consumed: " << value << std::endl;
}
// int main() {
// std::thread p(producer);
// std::thread c(consumer);
// p.join();
// c.join();
// return 0;
// }3. std::scoped_lock
对于需要同时锁定多个互斥锁以避免死锁的场景,C++17引入了
std::scoped_lock
std::mutex mtx1;
std::mutex mtx2;
void func_with_two_locks() {
// 自动以死锁安全的方式锁定mtx1和mtx2
std::scoped_lock lock(mtx1, mtx2);
// 临界区
std::cout << "Thread " << std::this_thread::get_id() << " acquired both locks." << std::endl;
// ...
}std::mutex::lock()
unlock()
直接使用
std::mutex::lock()
std::mutex::unlock()
主要问题出在异常安全和代码维护上:
异常安全问题: 假设你在
lock()
unlock()
unlock()
std::mutex mtx_dangerous;
void dangerous_function() {
mtx_dangerous.lock(); // 加锁
try {
// 某些操作,可能抛出异常
if (true) { // 模拟异常条件
throw std::runtime_error("Something went wrong!");
}
// ... 更多操作 ...
} catch (...) {
// 如果这里捕获了异常,但忘记了解锁,那么问题就大了
// mtx_dangerous.unlock(); // 很容易忘记这一行
throw; // 重新抛出异常
}
mtx_dangerous.unlock(); // 如果没有异常,才会执行到这里
}在上面的例子中,如果
throw std::runtime_error
unlock()
代码维护与可读性: 随着代码量的增加和复杂度的提高,确保每个
lock()
unlock()
unlock()
多返回路径问题: 一个函数可能有多个
return
return
unlock()
相比之下,
std::lock_guard
std::unique_lock
死锁是多线程编程中最令人头疼的问题之一,它通常发生在两个或更多线程互相等待对方释放资源时,导致所有线程都无法继续执行。避免死锁,我觉得更多是一种设计哲学和习惯,而不是单纯的技术手段。
死锁发生的四个必要条件(Coffman条件):
要避免死锁,我们通常会尝试破坏其中一个或多个条件。
实践中避免死锁的策略:
保持一致的加锁顺序(Consistent Lock Ordering): 这是最常用也最有效的策略。如果你的线程需要同时获取多个互斥锁,那么所有线程都应该以相同的顺序来获取这些锁。
std::mutex mtxA, mtxB;
void func1() {
std::lock_guard<std::mutex> lockA(mtxA); // 先锁A
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟工作
std::lock_guard<std::mutex> lockB(mtxB); // 再锁B
std::cout << "Func1 acquired A then B." << std::endl;
}
void func2() {
// 如果这里颠倒顺序,就可能死锁
// std::lock_guard<std::mutex> lockB(mtxB);
// std::lock_guard<std::mutex> lockA(mtxA);
// 正确做法:保持与func1相同的顺序
std::lock_guard<std::mutex> lockA(mtxA); // 先锁A
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟工作
std::lock_guard<std::mutex> lockB(mtxB); // 再锁B
std::cout << "Func2 acquired A then B." << std::endl;
}如果
func2
mtxB
mtxA
func1
mtxA
mtxB
使用std::lock()
std::lock(m1, m2, ...)
std::unique_lock
std::defer_lock
std::mutex mtx_x, mtx_y;
void swap_data(int& data_x, int& data_y) {
// std::lock 会原子性地锁定所有提供的互斥锁,避免死锁
std::unique_lock<std::mutex> lock_x(mtx_x, std::defer_lock);
std::unique_lock<std::mutex> lock_y(mtx_y, std::defer_lock);
std::lock(lock_x, lock_y); // 同时锁定,避免死锁
// 此时两个锁都被持有
std::swap(data_x, data_y);
std::cout << "Data swapped by thread " << std::this_thread::get_id() << std::endl;
// lock_x和lock_y在析构时会自动释放
}C++17的
std::scoped_lock
避免在持有锁时进行耗时操作或I/O操作: 锁的粒度应该尽可能小。在持有锁的临界区内,只进行必要的操作,尽快释放锁。长时间持有锁会增加其他线程等待的时间,也增加了死锁的可能性。
使用std::try_lock()
std::timed_mutex
std::mutex mtx_a, mtx_b;
void try_to_do_something() {
if (mtx_a.try_lock()) { // 尝试获取锁A
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟一些工作
if (mtx_b.try_lock()) { // 尝试获取锁B
std::cout << "Acquired both A and B." << std::endl;
mtx_b.unlock();
} else {
std::cout << "Could not acquire B, releasing A." << std::endl;
}
mtx_a.unlock();
} else {
std::cout << "Could not acquire A." << std::endl;
}
}这种方式虽然可以避免死锁,但代码会变得复杂,且可能导致活锁(livelock,线程反复尝试失败)。
避免不必要的嵌套锁: 尽量减少在一个锁的临界区内再尝试获取另一个锁的情况。如果确实需要,请确保遵循一致的加锁顺序。
资源分层: 为资源定义一个层次结构。线程总是按照从高到低的顺序获取资源(锁)。
死锁问题没有一劳永逸的解决方案,它需要开发者在设计并发系统时就进行周密的考虑。我的经验是,保持简单、一致的加锁顺序,并优先使用
std::scoped_lock
std::lock
C++标准库提供了多种多线程同步机制,它们各有侧重,适用于不同的并发场景。了解它们的特点和适用范围,能帮助我们更高效、安全地构建并发程序。
std::condition_variable
std::mutex
std::unique_lock
wait()
unique_lock
std::atomic
int
bool
std::atomic
std::mutex
std::atomic
load()
store()
exchange()
compare_exchange_weak()
compare_exchange_strong()
std::promise
std::future
std::promise
std::future
std::async
std::promise
std::future
std::future
get()
std::shared_future
future
std::shared_mutex
std::shared_timed_mutex
std::shared_lock
std::unique_lock
lock()
unlock()
std::latch
std::barrier
std::latch
std::barrier
std::latch
std::barrier
latch
wait()
count_down()
barrier
这些机制各有千秋,选择哪种取决于具体的同步需求。通常,我会先考虑
std::atomic
std::mutex
std::condition_variable
std::shared_mutex
latch
barrier
以上就是如何在C++中安全地使用互斥锁_C++多线程同步与互斥锁的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号