我使用的是Rust,但是Rust实现了C原子内存模型,所以我将用C来提出我的问题。
我有一个原子对象M
。我想在M
上发出一个伪加载/存储操作,以便该操作将"读取"的存储将在此操作之前发生,并且将"读取"此存储的所有加载将在此操作之后发生。基本上,我想要一个memory_order_acq_rel
,但**不更改M
的值。
第一部分很简单:一个memory_order_acquire
加载就足够了,但是对于第二部分,我需要一个memory_order_release
存储,我不知道原子的当前值,所以我不能存储它。
我知道我可以用一个compare-exchnage循环来实现,这个循环加载M
的值并将其存储回去:
void create_acq_rel(std::atomic<int>& object)
{
int value = object.load(std::memory_order_acquire);
while (!object.compare_exchange_weak(
value, value,
std::memory_order_release, std::memory_order_acquire
))
{
}
}
然而,这种方法的一个明显的缺点是,它在没有实际需要的情况下生成了一个比较交换循环。
一开始我以为围栏会有帮助,但看起来围栏需要实际的加载/存储才能同步,这是真的吗?
我不想更改之前/之后应该同步的代码,只想更改这部分代码,因为我认为这样会更简单(我甚至更喜欢比较交换循环来更改该代码,因为a)它是更多的代码,b)它在热路径中,而此代码不在)。
背景:我有两个无锁链表(一个部分空块列表和一个完整块列表,在一个竞技场中)。线程通常遍历第一个列表(以找到要分配的位置),但我可以将元素从第一个列表移到第二个列表(当块变满时)并且当前遍历它的线程将在第二列表中继续其遍历。第一个列表在列表头上完全同步:只有在初始化所有先前的元素之后,才向列表添加新元素,因此我可以确信遍历该列表的线程将只访问完全初始化的元素,当它们加载列表头时,其元素在被放入列表中之前被初始化,其后的所有元素(我附加在列表的开头)在它之前被初始化,但是有时候我会把一个元素直接附加到第二个列表中(当一个元素太大而无法放入块中时,我会专门为它分配一个块),现在遍历第一个列表并在第二个列表中继续遍历的线程可能会看到它未初始化,因为它不像其他元素那样与第一个列表的头部同步。我希望添加这个元素以参与元素初始化链,这样,先前元素的初始化发生在它之前,它发生在将来元素的初始化之前。(例如,通过在下一个指针上同步),但正如我所说的,我只想触及将元素直接追加到第二个列表的代码。
1条答案
按热度按时间voj3qocg1#
您可以使用
fetch_add
将0
添加到如下所示的值这将执行读取-修改-写入操作,并根据第二个参数中指定的顺序值影响内存。