std::shared_lock和std::shared_mut
2023-10-27 本文已影响0人
FredricZhu
当多线程访问时,大多数线程的行为是读取,少量写入时,可以使用共享锁。
共享锁的机制是,允许多个线程一起读。但当有一个线程,要写时,其他线程都在旁边看着,不能写,也不能读。
例如DNS缓存的机制就很符合这个场景。平时不咋更新,大部分时间都在读。
详细请看下面共享锁的例子。
conanfile.txt
[requires]
boost/1.72.0
[generators]
cmake
CMakeLists.txt
cmake_minimum_required(VERSION 3.3)
project(3_13_shared_lock)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")
set ( CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_STANDARD 17)
add_definitions(-g)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include_directories(${INCLUDE_DIRS})
LINK_DIRECTORIES(${LINK_DIRS})
file( GLOB main_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
foreach( main_file ${main_file_list} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${main_file})
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${main_file})
target_link_libraries(${file} ${CONAN_LIBS} pthread)
endforeach( main_file ${main_file_list})
main.cpp
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <sstream>
#include <functional>
#include <chrono>
class dns_entry {
std::string host_name;
public:
dns_entry(): host_name("localhost") {}
dns_entry(std::string const& host_name_): host_name(host_name_) {}
std::string get_host_name() {
return host_name;
}
};
class dns_cache {
std::map<std::string, dns_entry> entries;
std::shared_mutex entry_mutex;
public:
dns_entry find_entry(std::string const& domain) {
// 读取使用std::shared_lock,可以多个线程一起读取
std::shared_lock<std::shared_mutex> lk(entry_mutex);
std::map<std::string, dns_entry>::const_iterator const it = entries.find(domain);
return (it==entries.end()) ? dns_entry(): it->second;
}
void update_or_add_entry(std::string const& domain, dns_entry const& dns_detail) {
// 写入使用std::lock_guard,只能有一个线程写入,其他线程围观
// 否则两个一起写会造成corruption data
// 一读一写会读出未完成的数据,产生未定义行为
std::lock_guard<std::shared_mutex> lk(entry_mutex);
entries[domain] = dns_detail;
}
};
int main(int argc, char* argv[]) {
std::vector<std::thread> write_threads;
std::vector<std::thread> read_threads;
dns_cache dns_c;
for(int i=0; i<5; ++i) {
write_threads.push_back(std::thread([&, i](){
std::stringstream domain_ss;
std::stringstream entry_ss;
domain_ss << "domain " << i;
entry_ss << "entry " << i;
dns_entry entry_(entry_ss.str());
dns_c.update_or_add_entry(domain_ss.str(), entry_);
}));
read_threads.push_back(std::thread([&](){
std::stringstream domain_ss_read;
domain_ss_read << "domain 0";
auto entry_ = dns_c.find_entry(domain_ss_read.str());
std::cout << entry_.get_host_name() << std::endl;
domain_ss_read.str("domain 1");
entry_ = dns_c.find_entry(domain_ss_read.str());
std::cout << entry_.get_host_name() << std::endl;
}));
}
std::for_each(write_threads.begin(), write_threads.end(), std::mem_fn(&std::thread::join));
std::for_each(read_threads.begin(), read_threads.end(), std::mem_fn(&std::thread::join));
return EXIT_SUCCESS;
}
程序输出如下,
他的输出不是确定的,主要是看write_thread和read_thread谁先执行。如果read_thread先执行,可能会读到localhost
如果write_thread先写入entry 0和entry 1, 那么就会读到entry 0和entry 1。这也符合程序的期望。
![](https://img.haomeiwen.com/i8982195/2ad7ff2762b62ab6.png)