悠悠楠杉
c++中std::shared_mutex(读写锁)的使用_c++多线程读写锁同步实例,读写锁 c++11
在现代C++开发中,多线程编程已成为提升程序性能的重要手段。然而,多个线程同时访问共享资源时,如何保证数据的一致性和线程安全,是一个必须面对的问题。传统的互斥锁(如std::mutex)虽然能有效防止竞态条件,但在“读多写少”的场景下,其独占特性会严重限制并发效率。为此,C++17引入了std::shared_mutex,提供了一种更精细的同步机制——读写锁。
std::shared_mutex允许同时有多个读线程访问共享资源,但写操作必须独占访问。这种机制非常适合诸如缓存系统、配置管理器或数据库连接池等高频读取、低频更新的场景。通过合理使用读写锁,我们可以在保证线程安全的前提下,显著提升程序的并发性能。
下面通过一个具体的示例来展示std::shared_mutex的实际应用。假设我们正在开发一个线程安全的配置管理类,该类维护一个全局配置字典,多个工作线程需要频繁读取配置项,而管理线程偶尔会更新配置。
cpp
include
include
include
include
include
include
include
class ConfigManager {
private:
std::unorderedmap<std::string, std::string> configMap;
mutable std::sharedmutex configMutex; // mutable 允许在 const 成员函数中加锁
public:
// 读取配置(允许多个线程同时读)
std::string get(const std::string& key) const {
std::shared_lock
auto it = configMap.find(key);
if (it != configMap.end()) {
return it->second;
}
return "";
}
// 更新配置(写操作需独占访问)
void set(const std::string& key, const std::string& value) {
std::unique_lock<std::shared_mutex> lock(configMutex); // 独占锁
configMap[key] = value;
std::cout << "Config updated: " << key << " = " << value << std::endl;
}
// 批量加载配置
void loadDefaults() {
set("host", "localhost");
set("port", "8080");
set("timeout", "30");
}
};
在这个例子中,get方法被声明为 const,因为它不修改对象状态。为了在 const 方法中使用锁,我们将 configMutex 声明为 mutable。读操作使用 std::shared_lock 获取共享锁,允许多个线程同时进入;写操作则使用 std::unique_lock 获取独占锁,确保在写入期间没有其他读或写线程干扰。
接下来,我们模拟多个读线程和一个写线程并发访问配置管理器:
cpp
void reader(ConfigManager& mgr, int id) {
for (int i = 0; i < 5; ++i) {
std::string host = mgr.get("host");
std::string port = mgr.get("port");
std::cout << "Reader " << id << " read: host=" << host << ", port=" << port << std::endl;
std::thisthread::sleepfor(std::chrono::milliseconds(100));
}
}
void writer(ConfigManager& mgr) {
std::thisthread::sleepfor(std::chrono::seconds(1));
mgr.set("port", "9090");
std::thisthread::sleepfor(std::chrono::seconds(1));
mgr.set("timeout", "60");
}
主函数启动多个读线程和一个写线程:
cpp
int main() {
ConfigManager config;
config.loadDefaults();
std::vector<std::thread> threads;
// 启动5个读线程
for (int i = 0; i < 5; ++i) {
threads.emplace_back(reader, std::ref(config), i);
}
// 启动1个写线程
threads.emplace_back(writer, std::ref(config));
// 等待所有线程结束
for (auto& t : threads) {
t.join();
}
return 0;
}
运行程序可以看到,多个读线程几乎同时输出配置信息,而在写线程修改配置后,后续的读取立即反映新值。这说明读写锁成功实现了读操作的并发与写操作的安全隔离。
需要注意的是,std::shared_mutex并非万能。在写操作频繁的场景下,由于写线程可能长时间阻塞读线程,反而会导致性能下降。此外,不同平台对std::shared_mutex的实现效率也存在差异,建议在关键路径上进行性能测试。
