在多线程编程中,互斥锁(mutex)是确保线程安全、避免数据竞争的重要工具。C++标准库提供了多种互斥锁,每种都有其特定的应用场景和特点。
主要有以下几种互斥锁(Mutex):
- std::mutex:最基本的互斥锁,用于保护临界区,确保同一时间只有一个线程可以访问被保护的资源。
- std::timed_mutex:支持超时机制的互斥锁,可以尝试在给定时间内锁定互斥锁。如果在指定时间内没有成功获取锁,则返回失败。
- std::recursive_mutex:递归互斥锁,同一线程可以多次获取锁而不会发生死锁,通常用于递归函数中。
- std::recursive_timed_mutex:支持超时机制的递归互斥锁,结合了递归锁和超时锁的特性。
- std::shared_mutex(C++17 引入):允许多个线程同时读取,但只有一个线程可以写入。适用于读多写少的场景。
- std::shared_timed_mutex(C++17 引入):支持超时机制的共享互斥锁,可以在给定时间内尝试获取读锁或写锁。
这些是C++标准库中提供的几种主要的互斥锁类型。每种锁都有其特定的应用场景和使用方法,选择合适的互斥锁类型对于实现高效、安全的多线程程序非常重要。
一、基本互斥锁(std::mutex)
std::mutex是最基本的互斥锁,主要用于保护临界区,确保同一时间只有一个线程可以访问共享资源。
特点:
- 简单易用,适用于大多数场景。
- 不能递归锁定,同一线程多次尝试锁定会导致死锁。
示例代码:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::lock_guard<std::mutex> lock(mtx); // 自动管理锁的获取和释放
std::cout << "Thread ID: " << id << std::endl;
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
二、带超时机制的互斥锁(std::timed_mutex)
std::timed_mutex在std::mutex的基础上增加了超时功能,允许线程在指定时间内尝试获取锁,如果在超时时间内未成功获取锁,则返回失败。
特点:
- 适用于需要设置锁获取超时时间的场景。
- 提供try_lock_for和try_lock_until两种超时尝试获取锁的方法。
示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::timed_mutex tmtx;
void try_to_lock(int id) {
if(tmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " locked the mutex" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tmtx.unlock();
} else {
std::cout << "Thread " << id << " could not lock the mutex" << std::endl;
}
}
int main() {
std::thread t1(try_to_lock, 1);
std::thread t2(try_to_lock, 2);
t1.join();
t2.join();
return 0;
}
三、递归互斥锁(std::recursive_mutex)
std::recursive_mutex允许同一线程多次获取锁而不会发生死锁,这对于递归函数或需要多次锁定的场景非常有用。
特点:
- 适用于递归调用和需要多次锁定的场景。
- 需要注意避免滥用,因为递归锁的使用会增加锁定次数的复杂性。
示例代码:
#include <iostream>
#include <thread>
#include <mutex>
std::recursive_mutex rmtx;
void recursive_function(int depth) {
rmtx.lock();
std::cout << "Depth: " << depth << std::endl;
if (depth > 0) {
recursive_function(depth - 1);
}
rmtx.unlock();
}
int main() {
std::thread t(recursive_function, 5);
t.join();
return 0;
}
四、带超时机制的递归互斥锁(std::recursive_timed_mutex)
std::recursive_timed_mutex结合了std::recursive_mutex和std::timed_mutex的特性,支持递归锁定和超时机制。
特点:
- 适用于递归调用和需要超时机制的场景。
- 提供超时尝试获取递归锁的方法。
示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::recursive_timed_mutex rtmmtx;
void try_recursive_lock(int id, int depth) {
if (rtmmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " locked at depth " << depth << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (depth > 0) {
try_recursive_lock(id, depth - 1);
}
rtmmtx.unlock();
} else {
std::cout << "Thread " << id << " could not lock at depth " << depth << std::endl;
}
}
int main() {
std::thread t1(try_recursive_lock, 1, 3);
std::thread t2(try_recursive_lock, 2, 3);
t1.join();
t2.join();
return 0;
}
五、共享互斥锁(std::shared_mutex)
std::shared_mutex允许多个线程同时读取,但只有一个线程可以写入。这在读多写少的场景下非常有用。
特点:
- 适用于读多写少的场景。
- 读操作和写操作使用不同的锁定机制。
示例代码:
#include <iostream>
#include <thread>
#include <shared_mutex>
std::shared_mutex shmtx;
void read_shared(int id) {
std::shared_lock<std::shared_mutex> lock(shmtx); // 共享锁
std::cout << "Thread " << id << " is reading" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void write_shared(int id) {
std::unique_lock<std::shared_mutex> lock(shmtx); // 独占锁
std::cout << "Thread " << id << " is writing" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
std::thread readers[5], writer(write_shared, 1);
for (int i = 0; i < 5; ++i) {
readers[i] = std::thread(read_shared, i + 2);
}
writer.join();
for (auto& reader : readers) {
reader.join();
}
return 0;
}
六、带超时机制的共享互斥锁(std::shared_timed_mutex)
std::shared_timed_mutex结合了std::shared_mutex和std::timed_mutex的特性,支持超时机制。
特点:
- 适用于读多写少且需要超时机制的场景。
- 提供超时尝试获取共享锁的方法。
示例代码:
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <chrono>
std::shared_timed_mutex shtmmtx;
void try_read_shared(int id) {
if (shtmmtx.try_lock_shared_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " is reading" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
shtmmtx.unlock_shared();
} else {
std::cout << "Thread " << id << " could not read" << std::endl;
}
}
void try_write_shared(int id) {
if (shtmmtx.try_lock_for(std::chrono::milliseconds(100))) {
std::cout << "Thread " << id << " is writing" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
shtmmtx.unlock();
} else {
std::cout << "Thread " << id << " could not write" << std::endl;
}
}
int main() {
std::thread readers[5], writer(try_write_shared, 1);
for (int i = 0; i < 5; ++i) {
readers[i] = std::thread(try_read_shared, i + 2);
}
writer.join();
for (auto& reader : readers) {
reader.join();
}
return 0;
}
总结
C++标准库提供了多种类型的互斥锁,每种锁都有其特定的用途和特点。选择合适的互斥锁类型可以有效提高程序的并发性能和安全性。