考虑以下对多线程搜索算法的无锁哈希表的尝试(受本文启发)
struct Data
{
uint64_t key;
uint64_t value;
};
struct HashEntry
{
uint64_t key_xor_value;
uint64_t value;
};
void insert_data(Data const& e, HashEntry* h, std::size_t tableOffset)
{
h[tableOffset].key_xor_value = e.key ^ e.value;
h[tableOffset].value = e.value;
}
bool data_is_present(Data const& e, HashEntry const* h, std::size_t tableOffset)
{
auto const tmp_key_xor_value = h[tableOFfset].key_xor_value;
auto const tmp_value = h[tableOffset].value;
return e.key == (tmp_key_xor_value ^ tmp_value);
}
这个想法是HashEntry
结构存储结构的两个 64 位字的异或组合Data
。如果两个线程对结构的两个 64 位字进行了交错读取/写入HashEntry
,则想法是读取线程可以通过再次 XOR-ing 并与原始key
. 因此,可能会因哈希条目损坏而降低效率,但在解码后的检索密钥与原始密钥匹配的情况下仍能保证正确性。
该论文提到它基于以下假设:
对于本讨论的其余部分,假设 64 位内存读/写操作是原子操作,即整个 64 位值在一个周期内读/写。
我的问题是:上面没有明确使用的代码是否std::atomic<uint64_t>
保证在 C++11 中是线程安全的?或者单独的 64 位字是否会被同时读/写损坏?即使在 64 位平台上?这与旧的 C++98 标准有何不同?
来自标准的报价将不胜感激。
更新:根据Hans Boehm 关于“良性”数据竞争的这篇令人惊叹的论文,一个简单的被咬的方法是让编译器取消 XORinsert_data()
并data_is_present()
始终返回true
,例如,如果它找到一个本地代码片段,例如
insert_data(e, h, t);
if (data_is_present(e, h, t)) // optimized to true as if in single-threaded code
read_and_process(e, h, t); // data race if other thread has written