读书笔记《C++并发编程实战》(5) - 内存模型与原子类型操作

内存模型:
    C++内存位置布局,参见struct结构体/class类成员/全局、静态变量在内存中的布局。
    多个线程修改某位置的值,应按照某个顺序修改或者读取,否则可能出现数据竞争或未定义行为。

原子操作:
    类似于事务操作,对对象值的修改或者读取都是原子的,不会出现修改部分或读取到失效值的情况。
    标准原子类型std::atomic<>模板类型,提供了多个内置类型的特化版本的类型,如atomic_bool,atomic_int,atomic_size_t等,此外还支持用户
    自定义类型的原子变种,原子类型操作部分接口提供了多种可选的内存序的参数,默认使用的是memory_order_seq_cst;
    
    std::atomic_flag基本的原子类型,仅代表布尔标识,有两种状态:设置test_and_set、清除clear;此外初始化需要通过ATOMIC_FLAG_INIT初始化该
    原子类型实例。可以实现自旋锁,但因其限制性,一般情况下不会去使用该类型,而是使用std::atomic<bool>类型(std::atomic_bool)。
    
    std::atomic<bool>:提供写操作store、读修改写操作exchange、查询(载入)操作load或隐式转为bool类型,此外还有compare_exchange_strong,
    compare_exchange_weak比较判断交换函数。
    std::atomic<T*>提供和std::atomic<bool>类似的接口外,还支持fetch_add、fetch_sub、++/--操作。
    
    其他内置原子类型如atomic_int,可能有额外的操作接口,如fetch_add、fetch_sub、fetch_and、fetch_xor等复合赋值、前/后缀自增/减等。
    
    用户自定义类型的原子类型:需满足按位赋值、按位比较的特性,也即自定义类型必须是trivial的,没有虚函数或包含虚函数的成员。
    (std::atomic<float/double>不建议使用、类似std::atomic<std::vector<int>>的也不能使用)。
    
    其他的原子操作的自由函数:形如:std::atomic_load、std::atomic_is_lock_free、std::atomic_compare_exchange_weak、
    std::atomic_flag_clear_explicit等。
    
    原子操作不仅避免数据竞争,还会强制线程间的操作顺序。
    
原子操作内存顺序(std::memory_order):
    编译器优化、cpu指令流水线可能会产生指令乱序,以优化执行效率;
    C++中提供三种模型:顺序一致顺序、获得-释放顺序、松散顺序,表现为6种内存顺序,不同内存顺序在不同的CPU架构上有着可能不同的成本,如;
    memory_order_relaxed,memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel,memory_order_seq_cs。
    
    顺序一致顺序:
        默认的顺序memory_order_seq_cs,作为最直观的排序,程序的行为与单个线程顺序执行的顺序一致的,当某个线程在另一个线程操作之前,则
        其顺序必须对其他线程可见,此外要求线程间的全局同步,在某些多处理器的系统中需要处理器间的密集和耗时的通信,有一定的同步成本。
    
    memory_order_relaxed:
        单个线程的原子操作还是按照happens-before关系,但不同线程间的执行关系是任意的。要求同一个线程对单个原子的访问不能被重排,一旦
        给定的线程已看到院子办理的的定制,该线程之后的读取就不能获取该变量更早的值。在没有额外的同步情况下,每个变量的修改顺序是使用
        memory_order_relaxed。
    
    内存屏障:std::atomic_thread_fence,作为全局操作,能在执行该屏障的线程里影响其他原子操作的顺序,以限制被编译器或者硬件重新排序。

猜你喜欢

转载自www.cnblogs.com/haomiao/p/11647403.html