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

dgsult0t  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(144)

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

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

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

o0lyfsai

o0lyfsai1#

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

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

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

const-correctness注意事项

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

  1. std::as_const(mp)["someKey"]); // error
  2. const auto& cmp = mp;
  3. cmp["someKey"]; // error

std::tuple注意事项

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

  1. struct entry {
  2. int a, b; // obviously, use better names than a, b, c
  3. std::atomic<int> c;
  4. };
  5. // ...
  6. if (auto it = mp.find("someKey"); it != mp.end()) {
  7. it->c.store(2);
  8. }

展开查看全部

相关问题