Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;std::内存\u顺序\u松弛和跳过/停止标志_C++_Multithreading_Stdatomic_Memory Barriers - Fatal编程技术网

C++ C++;std::内存\u顺序\u松弛和跳过/停止标志

C++ C++;std::内存\u顺序\u松弛和跳过/停止标志,c++,multithreading,stdatomic,memory-barriers,C++,Multithreading,Stdatomic,Memory Barriers,使用std::memory\u order\u relaxed作为跳过标志可以吗,如iterate: constexpr static const std::size_t capacity = 128; std::atomic<bool> aliveness[capacity]; T data[capacity]; // immutable/atomic/etc. template<class Closure> void

使用
std::memory\u order\u relaxed
作为跳过标志可以吗,如
iterate

    constexpr static const std::size_t capacity = 128; 
    std::atomic<bool> aliveness[capacity];
    T data[capacity];     // immutable/atomic/etc.

    template<class Closure>
    void iterate(Closure&& closure){
        for(std::size_t i = 0; i<capacity; i++){
            if (!aliveness[i].load(std::memory_order_relaxed)) continue;                    
            closure( data[i] );
        }
    }

    void erase(std::size_t index){
        aliveness[index].store(false, std::memory_order_relaxed);
    }
constepr静态const std::size\u t capacity=128;
标准:原子活度[容量];
T数据[容量];//不变的/原子的/等等。
模板
无效迭代(闭包&闭包){
对于(std::size_t i=0;i假设:当您使用
erase
时,其他线程可以在任何时候运行
iterate()
(问题的早期版本没有指定不可变。如果更新
数据[i]
的锁定(或缺少锁定)未按顺序写入
alive[i],此答案仍然相关

如果数据确实是不可变的,那么
mou-released
肯定是可以的,除非您需要这些存储的全局可见性,以便根据线程正在执行的其他操作来排序。
mou-released
存储最终将始终对其他线程可见(并且在当前CPU上,将很快做到这一点)


如果您要修改非原子<代码>数据[i] <代码>,而<代码>活着[i] <代码>为false,则需要确保其他线程在修改时不使用其值。这将是C++中的UB,而实际硬件上的实际正确性问题取决于T和Cuth<<代码> >

Acquire semantics将用于
迭代
。对
数据[i]
的访问逻辑上发生在
活动[i]
之后,因为单向屏障的方向正确

但是
erase
中的存储是一个问题。在对
数据[i]
进行任何修改之前,它需要全局可见。但是允许发布存储与以后的存储重新排序。您需要的是

如果
T
是一种原子类型,那么
data[i]
的发布存储就可以了。但是不要这样做;如果
T
太大而不能成为无锁原子,那就糟糕了

在大多数实现中,seq cst存储也可以工作,但我认为这只是一个实现细节。它通常会产生一个存储+一个完整的asm指令(例如x86 MFENCE)。因此它工作的唯一原因是编译器将seq cst存储实现为存储+
thread_fence(seq_cst)


请注意,如果
closure
修改
data[]
,则
iterate
是不安全的,除非一次只能有一个线程调用它。在这种情况下,这有什么意义?因此,您可能应该使用

void iterate(Closure&& closure) const
{ ... }

因此,
iterate
只对容器的
const
对象起作用。

你说的“ok”是什么意思?为什么
size
是原子的,为什么每次
for
循环的循环迭代都要重新读取它(使用默认的seq\u cst加载)?@PeterCordes更新为更简单的情况。通过询问“ok”“如果在一个线程中执行<代码>擦除< /代码>,那么另一个线程< <代码>迭代< /COD>会看到该标志是否为假,以及多久?我们是否可能在一个线程中调用<代码>擦除< /代码>,但另一个线程没有看到该标志改变了吗?请考虑<代码>数据< /代码>不可变/原子。抱歉,这种不精确性。问题是RATH。er关于
alivity
线程间的可见性/传播(刚刚开始了解这一点)。对它的更改对所有线程都是不可见的,或者可以用以前的值错误读取。例如:我们在Thread1中将它设置为
false
,但Thread2读取以前的
true
值。@tower:Stores to
alivity[i] 
最终将对所有其他线程中的加载可见。(通常非常快,例如仅生产者CPU中存储缓冲区的长度+消费者CPU中早期读取的无序窗口的大小)。但是,加载的无序执行可能会导致不同的使用者看到不同的值。您必须小心如何定义“同步”因为执行顺序不正确。如果这些线程之间没有其他交互方式,这是一个毫无意义的问题,因为它们之间没有同步。因此,在示例中,这是一种机会主义/推测性擦除:),但如果我切换到acquire-release semantic,它将不会出现错误读数?@tower120:不,如果你没有修改
数据[I]
,那么将其称为“false”是没有意义的。线程之间没有顺序。这不是“错”要让迭代< /C>使用对应于<代码>生存> /CONT>值之前的<代码>数据< /代码>值,因为C++标准鼓励快速实现对其他线程可见的存储。您可以假设它不太快可见;它可能会使迭代< /COD>运行到Mak慢些。e确保它没有使用提前加载的
数据
值。我想,让迭代一个或两个额外的元素是正确的。。。
void iterate(Closure&& closure) const
{ ... }