【问题标题】:Atomic function without locks to change two independent memory locations无锁的原子函数改变两个独立的内存位置
【发布时间】:2017-05-01 09:02:28
【问题描述】:

我有一个名为updateEntry 的函数,它将一个值写入查找表。我想创建这个函数的多线程版本。 我正在研究原子操作__sync_bool_compare_and_swap,但我不确定如何在此处正确应用它。

理论上是否可以在不锁定的情况下自动实现此功能,因为它会更改两个独立的内存位置entryLookup[id]entry

   void updateEntry(Entry ** entryLookup, unsigned int id, int val1, short val2){
            Entry * entry     = entryLookup[id];
            entry->val1       = val1;
            entry->val2       = val2;
            entryLookup[id]  += sizeof(Entry);
   }

【问题讨论】:

  • 问题:1) 你能确保每个线程都能访问不相交的id 集合吗? 2) 你能保证修改val1val2 不会影响entryLookup operator[] 结果吗? 3) 你能确保修改entryLookup 的值不会影响任何并行线程(例如,新的id 不会被任何其他线程访问)?如果其中任何一条规则不匹配,我认为实现无锁并发会很困难。
  • @AdrianMaire 非常感谢您的回答。我构建了一个使用不相交 id 集的解决方案,但生成这种不相交集的成本很高。我想避免使用不相交的 ID 集。第 2 点是什么意思。)?
  • Q4 - 更新是否与查找同步?如果没有,您可以首先自动更新entryLookup[id],确保没有其他线程会使用相同的条目,然后在您闲暇时更新值(有点)。如果还要进行查找,则必须同时更新两个单独的位置,而这不能使用原始原子操作来完成。
  • 定义“无锁定”——你总是可以使用原子的“正在更新”标志
  • 我不知道entryLookup 是什么样子:我可以想象一个类,其中operator[] 被重载并访问val1。想象一个第一个线程,访问 id=5,它返回 0x42 处的条目,在修改 val1 之后,entryLookup 现在对应于(例如)id=10。下一个线程访问 id=10 也对应于 0x42 处的条目。现在两个线程在没有锁的情况下同时访问同一个对象。

标签: c++ atomic lock-free compare-and-swap


【解决方案1】:

为了使这个线程安全,您可以先增加entryLookup[id] 以确保稍后出现的任何其他线程不能更改相同的条目,然后填写值。返回旧值的地方需要原子加法:

void updateEntry(Entry ** entryLookup, unsigned int id, int val1, short val2)
{
    Entry * entry = __sync_fetch_and_add(&entryLookup[id], sizeof(Entry));
    entry->val1   = val1;
    entry->val2   = val2;
}

【讨论】:

  • 我刚刚注意到@eran 已经在 cmets 中给出了这个答案。
  • 这很简单,应该可以解决我的问题。非常感谢这个很棒的答案。
  • @peppincsoda 这里的问题是两个val 条目的修改从未发布(同步)。如何在另一个线程中安全地访问entry
  • 请注意,仅当更新阶段在执行查找之前结束时,这才是安全的。否则,查找可能会在设置其值之前看到一个条目。此外,正如@LWimsey 评论的那样,更新结束时应该存在一些写入障碍(即使没有明确请求刷新,这种代码也可能会起作用,但访问这些值仍将被视为数据竞争)。
  • 你说得对,我假设提问者不想在从多个线程更新结构时进行查找。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 2015-09-12
  • 2013-09-26
相关资源
最近更新 更多