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。这也符合程序的期望。

image.png
上一篇 下一篇

猜你喜欢

热点阅读