多个线程需要共享同一份配置,但是配置本身又需要更新,如何处理?比较理想的处理方式是 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})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。