这种原子的使用是否正确?

Is this use of atomic correct?

我有以下层次结构:

struct Point
{
  std::atomic<int> x;
  std::atomic<int> y;
  std::atomic<int> z;
}

class Data
{
 std::atomic<int> version;
 Point point;
}

unordered_map<std::string,Data> hashtable; // global

Thread1 是生产者:

线程 1 收到 std::pair<std::string,Point> new_data,它确实:

shared_hashtable[new_data.first].point.x.store(new_data.second.x,memory_order_release) // update X 
shared_hashtable[new_data.first].point.y.store(new_data.second.y,memory_order_release) // update Y
shared_hashtable[new_data.first].point.z.store(new_data.second.z,memory_order_release) // update Z
hashtable[new_data.first].version.store(hashtable[new_data.first].version+1,memory_order_release) // update the version of the data

线程 2 执行:

int local_version;
while(local_version!=shared_hashtable["string1"].version.load(memory_order_acquire))
{
  //...process data...
 local_version=shared_hashtable["string1"].version.load(memory_order_acquire);
 }

传递的内存顺序是否保证我的存储和加载不会被重新排序。 设计是否按预期工作:如果更新 hastable["string1"] 处的对象,我会在线程 2 中处理相应的数据吗?

hashtable 不受同时访问保护,其 operator[] 不是 const。如果您保证 (1) hashtable["string1"] 在这两个线程尝试访问它之前插入到 table 中,并且 (2) 在这两个期间没有其他线程写入 hashtable线程是 运行,hashtable 中的查找不会导致数据争用。如果您不能保证 (1) 和 (2),那么您需要使用互斥体来保护查找。 unordered_map 重新散列时使迭代器无效,但引用保持有效,直到引用项从映射中删除。

内存模型保证线程 1 中在 memory_order_release 写入 version 之前的写入对于线程 2 在 memory_order_acquire 从 [=] 读取相应值后是可见的21=]。即使对 Point 成员的访问是非原子的,情况也是如此。

但是,线程 2 中 Point 成员的读取可能会看到线程 1 中 later 写入的值,因此不能保证在线程 2 中读取的三个 Point 成员对应于线程 1 实际写入的特定 Point。我想你需要保证线程 2 处理的 Point 实际上是一些Point 由线程 1 编写,而不是多个不同点的值的集合(例如,x 来自版本 1,y 来自版本 2,z 来自版本 3) .更改设计来自

struct Point { atomic<int> x, y, x; };

struct Point { int x, y, x; };
struct Data {
  atomic<int> version;
  atomic<Point> point;
};

将确保 Point 读取实际上是 Point 写入。当然,它不一定是 Point 对应给定的 versionpoint 可能已经被 later 版本覆盖在线程 2 开始阅读它时线程 1。这可能会导致同一点被线程 2 处理两次,一次使用陈旧版本,然后在稍后的迭代中使用正确版本再次处理。如果需要确保每个版本最多处理一次,则必须验证 version 在 读取 point 后与 [=54] 相同 =]之前阅读point.

把它们放在一起,我们得到这个程序 (DEMO):

struct Point
{
  int x, y, z;
};

struct Data
{
  std::atomic<int> version;
  std::atomic<Point> point;
};

std::unordered_map<std::string, Data> hashtable;
std::mutex mtx;

Data& lookup(const std::string& key) {
  std::lock_guard<std::mutex> guard{mtx};
  return hashtable[key];
}

void thread1() {
  std::string key;
  Point point;
  std::tie(key, point) = // get new key/point pair
  auto& ref = lookup(key);
  ref.point.store(point, std::memory_order_relaxed);
  ref.version.fetch_add(1, std::memory_order_release);
}

void thread2() {
  auto& ref = lookup("string1");
  int local_version = 0;
  for (;;) {
    auto const version = ref.version.load(std::memory_order_acquire);
    if (local_version != version) {
      auto point = ref.point.load(std::memory_order_relaxed);
      if (version == ref.version.load(std::memory_order_acquire)) {
        local_version = version;
        // ...process point...
      }
    }
  }
}

你可以试试 https://github.com/Taymindis/atomic_hashtable

当多线程在缓冲区、简单和 Stable

上执行操作时,无需锁定即可读取、写入和删除的散列 table

API Readme.md

中给出的文件