mutex
std::mutex 是C++标准库中用于线程同步的互斥锁机制,主要用于保护共享资源,避免多个线程同时访问导致的竞态条件。
它提供了以下功能:
-
加锁(
lock
):阻塞当前线程,直到获取锁。 -
解锁(
unlock
):释放锁,允许其他线程获取锁。 -
尝试加锁(
try_lock
):尝试获取锁,如果锁已被占用则立即返回。
使用全局锁案例
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx; // 定义一个全局互斥锁
int shared_data = 0;
void increment() {
for (int i = 0; i < 10000; i++) {
mtx.lock(); // 加锁
shared_data++;
mtx.unlock(); // 解锁
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "共享数据: " << shared_data << endl; // 输出应该是20000
return 0;
}
使用传参锁案例
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int shared_data = 0;
void increment(mutex& mtx) {
for (int i = 0; i < 10000; i++) {
mtx.lock(); // 加锁
shared_data++;
mtx.unlock(); // 解锁
}
}
int main() {
mutex mtx; // 使用传参互斥锁
thread t1(increment,ref(mtx));//需要使用 ref 包装锁,不然会报错
thread t2(increment,ref(mtx));
t1.join();
t2.join();
cout << "共享数据: " << shared_data << endl; // 输出应该是20000
return 0;
}
timed_mutex
std::timed_mutex 是C++11引入的一种互斥锁类型,用于多线程编程中控制对共享资源的并发访问。它提供了超时机制,允许线程在尝试获取锁时设置一个时间限制,从而避免无限等待锁释放,降低死锁风险。
-
try_lock_for(duration)
:尝试在指定的时间内获取锁,如果超时则返回false
。 -
try_lock_until(time_point)
:尝试在指定的时间点之前获取锁,如果超时则返回false。
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;
timed_mutex mtx;
void func(int id) {
chrono::seconds timeout(3); // 设置超时时间为3秒
if (mtx.try_lock_for(timeout)) {
cout << "线程 " << id << " 成功获取锁,执行临界区代码..." << endl;
this_thread::sleep_for(chrono::seconds(5)); // 模拟工作负载
mtx.unlock();
}
else {
cout << "线程 " << id << " 超时未能获取锁,继续执行其他任务..." << endl;
}
}
int main() {
thread t1(func, 1);
thread t2(func, 2);
t1.join();
t2.join();
return 0;
}
recursive_mutex
- 调⽤⽅线程在从它成功调⽤ lock 或 try_lock 开始的时期⾥占有 recursive_mutex。此时期之内,线程可以进⾏对 lock 或 try_lock 的附加调⽤。所有权的时期在线程进⾏匹配次数的 unlock 调⽤时结束。
- 线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调⽤ lock)或收到 false 返回值(对于调⽤ try_lock)。
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
recursive_mutex rec_mutex;
void recursiveFunction(int count) {
if (count <= 0) return;
rec_mutex.lock();
cout << "Lock acquired. Count: " << count << endl;
// 递归调用
recursiveFunction(count - 1);
rec_mutex.unlock();
cout << "Lock released. Count: " << count << endl;
}
int main() {
thread t(recursiveFunction, 3);
t.join();
return 0;
}
lock_guard
std::lock_guard 是 C++ 标准库中用于管理互斥锁的 RAII(Resource Acquisition Is Initialization,资源获取即初始化)工具。它通过构造函数自动获取互斥锁,并在析构函数中自动释放锁,从而确保互斥锁的正确管理,避免因忘记解锁而导致的死锁问题。
主要特性
-
自动管理锁:通过构造函数获取锁,析构函数释放锁,无需手动调用
lock()
和unlock()
。 -
防止死锁:确保即使在异常情况下也能正确释放锁。
-
不可移动和复制:std::lock_guard 不支持拷贝或移动构造,确保锁的唯一性。
构造函数
explicit lock_guard(MutexType& mutex, std::adopt_lock_t tag = std::defer_lock);
-
MutexType& mutex:要管理的互斥锁对象,如mutex,timed_mutex,recersive_mutex。
-
std::adopt_lock_t tag:可选参数,表示当前线程已经持有锁,默认值是 std::defer_lock,表示 lock_guard 会在构造时自动尝试加锁。如果传入 std::adopt_lock,则表示当前线程已经通过其他方式(例如 std::lock 或手动调用 lock())持有锁,lock_guard 不会再次尝试加锁,而是直接接管锁的管理。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void print_block(int n, char c) {
lock_guard<mutex> lock(mtx); // 默认加锁,无需手动解锁
for (int i = 0; i < n; ++i) {
cout << c;
}
cout << '\n';
}
int main() {
thread t1(print_block, 50, '*');
thread t2(print_block, 50, '$');
t1.join();
t2.join();
return 0;
}
unique_lock
主要特性
-
延迟加锁:可以在构造时不立即加锁,而是通过显式调用
lock()
或unlock()
来控制锁的获取和释放。 -
支持条件变量:可以与
std::condition_variable
配合使用,支持线程间的同步。 -
可选锁管理:可以选择是否持有锁,或者在构造时直接接管已持有的锁。
-
支持多种互斥锁类型:可以与
std::mutex
、std::recursive_mutex
、std::timed_mutex
和std::shared_mutex
配合使用。
构造函数
template <typename Mutex>
unique_lock() noexcept; // 默认构造,不绑定任何锁
template <typename Mutex>
explicit unique_lock(Mutex& m, std::defer_lock_t); // 延迟加锁
template <typename Mutex>
unique_lock(Mutex& m, std::try_to_lock_t); // 尝试加锁,失败时不阻塞
template <typename Mutex>
unique_lock(Mutex& m, std::adopt_lock_t); // 假设已持有锁,不加锁
template <typename Mutex>
unique_lock(Mutex& m); // 默认加锁
template <typename Mutex>
unique_lock(Mutex& m, std::unique_lock<Mutex>&& other); // 移动构造
主要成员函数
-
lock()
:尝试加锁,如果锁已被占用则阻塞。 -
try_lock()
:尝试加锁,如果锁已被占用则立即返回false
。 -
unlock()
:释放锁。 -
owns_lock()
:检查是否持有锁。 -
mutex()
:获取绑定的互斥锁对象。 -
release()
:释放锁的所有权,但不释放锁本身。 -
swap()
:交换两个std::unique_lock
的状态。
lock和try_lock
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx1;
mutex mtx2;
void worker() {
lock(mtx1, mtx2); // 同时锁定两个锁
unique_lock<mutex> lock1(mtx1, std::adopt_lock); // 需要创建 unique_lock 自动释放锁
unique_lock<mutex> lock2(mtx2, std::adopt_lock);
cout << "线程进入临界区..." << endl;
this_thread::sleep_for(chrono::seconds(1)); // 模拟工作负载
cout << "线程退出临界区..." << endl;
}
int main() {
thread t1(worker);
thread t2(worker);
t1.join();
t2.join();
return 0;
}
call_once
std::call_once 是 C++11 引入的一个函数模板,用于确保某个操作(如初始化或配置加载)在多线程环境中只被调用一次。它结合了 std::once_flag,能够高效地实现线程安全的单次执行。
基本用法
std::call_once 的函数原型如下:
template <class Callable, class... Args>
void call_once(std::once_flag& flag, Callable&& f, Args&&... args);
-
std::once_flag
:一个标记对象,用于记录操作是否已经执行过。 -
Callable&& f
:需要执行的可调用对象(如函数、lambda 表达式、函数对象等)。 -
Args&&... args
:传递给可调用对象的参数。
工作原理
-
首次调用:如果 std::once_flag 标记的操作尚未执行,则 std::call_once 会调用
f
,并设置 flag 标记为“已执行”。 -
后续调用:如果 flag 已标记为“已执行”,则后续调用 std::call_once 的线程会直接跳过 f 的执行。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
std::once_flag flag;
void init(int id) {
cout << "Initialization function called once." << endl;
cout << "thread " << id << " is running." << endl;
}
void worker(int id) {
call_once(flag, init,id); // 确保 init 只被调用一次
}
int main() {
thread t1(worker, 1);
thread t2(worker, 2);
thread t3(worker, 3);
t1.join();
t2.join();
t3.join();
return 0;
}