1

多个线程需要共享同一份配置,但是配置本身又需要更新,如何处理?比较理想的处理方式是 copy on write,每次更新在原有的数据上拷贝一份,加上更新,然后再替换全局的那份配置。这样做的好处是读取配置完全不会被后台更新block住。要是用加锁的方式,万一后台更新线程哪里不靠谱,前台读配置的地方就被block住了。但是这样就带来了一个旧的配置对象什么时候被回收的问题。因为旧的配置对象可能被任意个线程在读取着,我们怎么知道什么时候这些线程都读完了呢?也许我们会得出一个结论,这个做法只在有GC的语言里可以搞。

但是,C++ 14 的 shared_ptr,atomic_load 和 atomic_store 提供了一个解决办法

#include <iostream>
#include <map>
#include <chrono>
#include <thread>
#include <memory>
#include <atomic>
#include <random>

using namespace std::chrono_literals;

class Config {
public:
    int id;

    Config(int id) {
        this->id = id;
        std::cout << "[" << std::this_thread::get_id() << "] " << id << " created" << std::endl;
        std::cout.flush();
    }

    ~Config() {
        std::cout << "[" << std::this_thread::get_id() << "] " << id << " deleted" << std::endl;
        std::cout.flush();
    }
};

std::shared_ptr<Config> globalCfg;

std::shared_ptr<Config> readCurrentCfg() {
    return std::atomic_load(&globalCfg);
}

void reader() {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    std::uniform_int_distribution<std::mt19937::result_type> dist(100, 20 * 100);
    auto cfg = readCurrentCfg();
    std::cout << "[" << std::this_thread::get_id() << "] " << cfg->id << " accessed" << std::endl;
    std::cout.flush();
    std::this_thread::sleep_for(1ms * dist(rng));
    std::cout << "[" << std::this_thread::get_id() << "] " << cfg->id << " accessed again" << std::endl;
    std::cout.flush();
}

void updateConfig(int id) {
    auto cfg = std::make_shared<Config>(id);
    std::atomic_store(&globalCfg, cfg);
}

void writer() {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    std::uniform_int_distribution<std::mt19937::result_type> dist(100, 5 * 100);
    for (int i = 1; i <= 15; i++) {
        std::this_thread::sleep_for(1ms * dist(rng));
        updateConfig(i);
    }
}

int main() {
    globalCfg = std::make_shared<Config>(0);

    std::mt19937 rng;
    rng.seed(std::random_device()());
    std::uniform_int_distribution<std::mt19937::result_type> dist(20, 100);
    std::vector<std::unique_ptr<std::thread>> threads;
    threads.push_back(std::make_unique<std::thread>(writer));
    for (int i = 1; i <= 30; i++) {
        threads.push_back(std::make_unique<std::thread>(reader));
        std::this_thread::sleep_for(1ms * dist(rng));
    }
    for (auto& thread : threads) {
        thread->join();
    }
    return 0;
}

运行结果

/home/xiaoju/.CLion2016.2/system/cmake/generated/test2-90d0d4c6/90d0d4c6/Debug/test2
[140460549330752] 0 created
[140460522039040] 0 accessed
[140460513646336] 0 accessed
[140460505253632] 0 accessed
[140460496860928] 0 accessed
[140460488468224] 0 accessed
[140460480075520] 0 accessed
[140460530431744] 1 created
[140460471682816] 1 accessed
[140460505253632] 0 accessed again
[140459993200384] 1 accessed
[140459984807680] 1 accessed
[140459976414976] 1 accessed
[140460522039040] 0 accessed again
[140460530431744] 2 created
[140459968022272] 2 accessed
[140459976414976] 1 accessed again
[140459959629568] 2 accessed
[140459951236864] 2 accessed
[140459942844160] 2 accessed
[140460530431744] 3 created
[140460488468224] 0 accessed again
[140459657656064] 3 accessed
[140459993200384] 1 accessed again
[140459649263360] 3 accessed
[140459984807680] 1 accessed again
[140459951236864] 2 accessed again
[140459640870656] 3 accessed
[140460471682816] 1 accessed again
[140460471682816] 1 deleted
[140459632477952] 3 accessed
[140459624085248] 3 accessed
[140459640870656] 3 accessed again
[140459615692544] 3 accessed
[140459607299840] 3 accessed
[140460480075520] 0 accessed again
[140459456329472] 3 accessed
[140460530431744] 4 created
[140459447936768] 4 accessed
[140459439544064] 4 accessed
[140459942844160] 2 accessed again
[140459968022272] 2 accessed again
[140459431151360] 4 accessed
[140459422758656] 4 accessed
[140459632477952] 3 accessed again
[140459414365952] 4 accessed
[140459405973248] 4 accessed
[140460530431744] 5 created
[140460496860928] 0 accessed again
[140459187894016] 5 accessed
[140460513646336] 0 accessed again
[140460513646336] 0 deleted
[140459439544064] 4 accessed again
[140459179501312] 5 accessed
[140460530431744] 6 created
[140459431151360] 4 accessed again
[140460530431744] 7 created
[140460530431744] 6 deleted
[140459959629568] 2 accessed again
[140459959629568] 2 deleted
[140459624085248] 3 accessed again
[140459414365952] 4 accessed again
[140459422758656] 4 accessed again
[140460530431744] 8 created
[140460530431744] 7 deleted
[140459456329472] 3 accessed again
[140459607299840] 3 accessed again
[140459657656064] 3 accessed again
[140459405973248] 4 accessed again
[140459649263360] 3 accessed again
[140459447936768] 4 accessed again
[140459447936768] 4 deleted
[140459179501312] 5 accessed again
[140460530431744] 9 created
[140460530431744] 8 deleted
[140460530431744] 10 created
[140460530431744] 9 deleted
[140459615692544] 3 accessed again
[140459615692544] 3 deleted
[140459187894016] 5 accessed again
[140459187894016] 5 deleted
[140460530431744] 11 created
[140460530431744] 10 deleted
[140460530431744] 12 created
[140460530431744] 11 deleted
[140460530431744] 13 created
[140460530431744] 12 deleted
[140460530431744] 14 created
[140460530431744] 13 deleted
[140460530431744] 15 created
[140460530431744] 14 deleted
[140460549330752] 15 deleted

Process finished with exit code 0

可以看到15个对象全部都在最后一次访问之后被释放了。本地 cmake 的文件

cmake_minimum_required(VERSION 3.6)
project(test2)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -stdlib=libc++ -pthread")

set(SOURCE_FILES main.cpp)
add_executable(test2 ${SOURCE_FILES})

taowen
4.1k 声望1.4k 粉丝

Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com


引用和评论

0 条评论