用cppmem伪代码编写:
int main()
{
atomic_int n = -1;
atomic_int n2 = 0;
{{{
{
n2.store(1, mo_relaxed);
if (n.load(mo_relaxed) != -1)
n.store(1, mo_release);
}
|||
{
n.store(0, mo_release);
int expected = 0;
do
{
desired = n2.load(mo_relaxed);
}
while (!n.compare_exchange_strong(&expected, desired, mo_acquire));
}
}}}
assert(n == 1);
}
换句话说,两个原子变量被初始化为n = -1和n2 = 0;
线程1首先将1写入n2,然后写入n,前提是n不(仍然)-1。
线程2首先将0写入n,然后加载n2并分配n = n2,前提是自从上次读取n(或当n仍为0以来)不改变n为止。
在两个线程都加入后,n在每个可能的执行中必须等于1。
此代码是我的一个开源项目的一部分,与将streambuf实现重置为无锁缓冲区的开始同时两个线程同时对其进行读写操作有关。该特定部分与“同步”(或刷新写入的输出)有关。
我对此进行了设计,并且在每个操作都相继一致时(通过了蛮力测试)它可以工作,但是我无法满足内存顺序要求:/。
答案 0 :(得分:1)
如果按以下顺序执行指令(和缓存更新),则可能触发此断言:
n2
的值从0
更改为1
。n
的值从-1
更改为0
。n2
(在n2.load(mo_relaxed)
中)。此时,由于没有同步,因此可以加载先前存储在n2
中的任何值(包括初始化值,请参见[intro.race]/1)。假设它加载了0
。n==0
(修改顺序为n
的最后一个),n2==0
,expected==0
,desired==0
交换指令。然后,比较交换成功,并将0
存储在n
中。两个线程执行结束时,您将得到n==0
和n2==1
。
具有顺序一致性,因为线程1看到了n2==1 && n==-1
,线程2看不到n2==0 && n==0
,所以我无法描述。
使用这种算法,我确信除了顺序一致性之外,无法使用其他任何内存顺序。
答案 1 :(得分:0)
使用我在https://plv.mpi-sws.org/rcmc/上找到的工具 通过实验,我发现最宽松的要求是:
线程1:
n2.store(1, std::memory_order_seq_cst);
if (n.load(std::memory_order_seq_cst) != -1)
n.store(1, std::memory_order_release);
线程2:
n.store(0, std::memory_order_seq_cst);
int expected = 0;
int desired;
do
{
desired = n2.load(std::memory_order_seq_cst);
}
while (!n.compare_exchange_strong(expected, desired,
std::memory_order_acquire, std::memory_order_relaxed));
编辑:
同一作者的最新工具(当然也更好) 现在可以从https://github.com/MPI-SWS/genmc
下载即使对于测试使用弱序原子的实时算法,该工具也非常快速且有用,例如,我在这里进行的操作:genmc_buffer_reset_test.c
给定行上的#include是生成的C文件,这些文件从我的C ++代码中提取并使用awk脚本转换为C,因为在我看来genmc只能(不幸地)仅用于C代码(?)。 / p>