c++ 在读锁区域中写入std::atomic线程是否安全

dgsult0t  于 6个月前  发布在  其他
关注(0)|答案(1)|浏览(51)

我有一个std::map<std::string, std::tuple<int, int, std::atomic<int>>> mp。这个Map可以并行读写,所以我使用了一个读写锁来保证它的线程安全。
我想知道我是否可以在读锁区域写入std::get<2>(mp["someKey"])

void read() {
    // read lock area
    std::shared_lock<std::shared_mutex> read_lock{mtx};
    // "someKey" exists already
    std::get<2>(mp["someKey"]).store(2);
}

字符串
我试着做了一些测试,似乎它的工作,但我不知道它是否是不正确的,并给我一个核心转储的一天。

o0lyfsai

o0lyfsai1#

首先,请注意mp["someKey"]修改了Map,并且不是线程安全的。如果还不存在键,operator[]将创建一个新的值初始化的键/值对。参见Why is std::map::operator[] considered bad practice?
相反,您应该使用mp.at("someKey")mp.find("someKey")

// OK, exception thrown if key doesn't exist yet
std::get<2>(mp.at("someKey")).store(2);

// OK, nothing happens if key doesn't exist yet
if (auto it = mp.find("someKey"); it != mp.end()) {
    std::get<2>(*it).store(2);
}

字符串
.store(2);是线程安全的,因为从多个线程同时写入std::atomic是安全的。std::shared_lock不是确保这一点的必要条件。但是,std::shared_lock确保周围的std::pair不会被另一个线程破坏,因此必须保留。

const-correctness注意事项

Const-correctness可以防止mp["someKey"]的错误。如果您只持有读锁,那么使用const&来防止意外修改是一个很好的做法:

std::as_const(mp)["someKey"]); // error

const auto& cmp = mp;
cmp["someKey"]; // error

std::tuple注意事项

变量模板之外的std::tuple总是有问题的。有意义的名字总是更好:

struct entry {
    int a, b; // obviously, use better names than a, b, c
    std::atomic<int> c;
};

// ...

if (auto it = mp.find("someKey"); it != mp.end()) {
    it->c.store(2);
}

相关问题