当前位置: 首页 > news >正文

如何自创网站3a汽车集团公司网络营销方案

如何自创网站,3a汽车集团公司网络营销方案,全网,学校网站建设和维护情况五、内存模型和原子操作 5.1 C中的标准原子类型 原子操作是不可分割的操作#xff0c;它或者完全做好#xff0c;或者完全没做。 标准原子类型的定义在头文件atomic中#xff0c;类模板std::atomicT接受各种类型的模板实参#xff0c;从而创建该类型对应…五、内存模型和原子操作 5.1 C中的标准原子类型 原子操作是不可分割的操作它或者完全做好或者完全没做。 标准原子类型的定义在头文件atomic中类模板std::atomicT接受各种类型的模板实参从而创建该类型对应的原子类型。 C为内建类型定义了特例化的标准原子类型并使用atomic_T作为对T类型特例化的别名 std::atomicbool→std::atomic_boolstd::atomicchar→std::atomic_char 对于一些内建的typedef类型C也提供了它们的特例化原子类型及别名如 std::atomicstd::size_t→std::atomic_size_t 标准原子类型是基于std::atomic模板类定义的该模板类支持初始化、值的存储与获取、比较交换操作等。std::atomicbool完全基于该主模板类定义所以std::atomicbool支持的操作就是所有标准原子类型支持的操作包含如下五种。 1. 初始化 原子对象的初始化是非原子的不同初始化方法的执行流程为 默认初始化。C20前只为静态变量和全局变量执行值初始化。C20后使用值初始化底层对象。{// 未初始化的原子对象std::atomic_bool abool; }使用内置类型初始化std::atomic_bool abool1 false; std::atomicbool abool2{true};因为原子类型上的操作全是原子化的在拷贝赋值的过程中需要从源对象读取值再写入目标对象。这是两个对象上的独立操作其组合不可能是原子化的所以原子对象禁止拷贝赋值和拷贝构造。 2. 值的存储与获取 有两种原子操作方法可以重新指定原子变量保存的值 T operator(T desired)如 abool true。为了避免多个线程修改造成获取的返回结果不可预测该赋值运算按值返回非原子类型的实参。void store(T desired)如 abool.store(true) 同样有两种原子操作方法可以获取原子变量保存的值 operator T()如(bool)abool。通过重载的类型转换运算符以非原子类型方式获取保存的值T load()如abool.load()。 可以原子性地执行读-修改-写操作即获取原子变量的原值并指定新值 T exchange(T desired)如abool.exchange(true)。返回非原子类型的原子变量底层值并设置原子变量的值为desired std::atomicint a 3; std::atomicint b{3}; std::cout [init] a: a.load() ; b: (int)b std::endl; // [init] a: 3; b: 3// 保存新的值 a 5; b.store(5); std::cout [set] a: a.load() ; b: (int)b std::endl; // [set] a: 5; b: 5// 获取新的值 int old_a a.exchange(7); int old_b b.exchange(7); std::cout [exchange] old_a: old_a ; old_b: old_b std::endl; // [exchange] old_a: 5; old_b: 5 std::cout [exchange] a: a.load() ; b: (int)b std::endl; // [exchange] a: 7; b: 7因为原子类型定义了类型转换运算符所以当使用std::cout可以直接输出std::atomicint等类型的变量这是因为隐式调用了类型转换运算符实际输出的是普通类型的变量 std::atomicint i 0; std::atomicdouble d 1.2;std::cout i: i ; d: d std::endl; // i: 0; d: 1.2 // 实际隐式调用了 cout.operator((int)i)3. 比较-交换Compare and Swap, CAS 比较交换操作是原子类型的编程基石有两个成员函数可以完成 bool compare_exchange_weak(T expected, T desired)bool compare_exchange_strong(T expected, T desired) 它们原子地逐字节比较原子对象与**expected**的值 如果相等将**desired保存到原子对象 **返回true如果不等将原子对象的值保存到**expected**返回false compare_exchange_weak可能会发生假性失败即当*this expected时也执行将原子对象的值保存到expected并返回false但是性能更好。如果**compare_exchange_weak用在循环**中就可以通过多次循环避免假性失败的问题。因为一些处理器不支持原子比较交换指令使用循环weak会更高效 compare_exchange_strong不会发生假性失败一定会严格的按照上述逻辑执行。如果对性能要求并不极致通常使用该版本会更简单。 两者的区别可以参考文章——c并发编程3. CAS原语 从以上逻辑可以看出如果在while循环中执行以上两种函数由于失败时会设置expected等于原子对象的值那么最多在第二次循环时一定会将desired写入原子对象。即 std::atomicint a_int 5; int exp 6, des 7; int loop_num 1; while(!a_int.compare_exchange_weak(exp, des)) {std::cout [ loop_num ] exp: exp std::endl;std::cout [ loop_num ] a_int: a_int.load() std::endl; } std::cout [end] a_int: a_int.load() std::endl; /* [1] exp: 5 [1] a_int: 5 [end] a_int: 7 */4. C20的新操作 C20起还添加了以下操作 void wait(T old)阻塞线程每次调用notify_xxx时去检查原子值若值已经与old不同退出阻塞否则继续阻塞。不调用notify_xxx时即使值改变了也不会影响wait。该函数可能会被虚假解锁即没有任何线程调用notify_xxx时可能会去执行原子值与old的对比而退出阻塞。void notify_one()和void notify_all()如果有正在阻塞的线程提醒一个或多个线程去检测原子值是否改变判断是否可以退出阻塞。 std::atomicint a_int 5;void Worker() {std::chrono::time_point beg std::chrono::steady_clock::now();a_int.wait(5);std::chrono::time_point end std::chrono::steady_clock::now();std::cout wait time: std::chrono::duration_caststd::chrono::milliseconds(end - beg) std::endl; }int main(int argc, char const *argv[]) {std::thread t(Worker);// 多次调用 notify_all 提醒原子变量去检测原子值std::this_thread::sleep_for(std::chrono::seconds(2));a_int.notify_all();std::this_thread::sleep_for(std::chrono::seconds(2));a_int.store(6);std::this_thread::sleep_for(std::chrono::seconds(2));a_int.notify_all();t.join();return 0; } /* wait time: 6001ms */5. 无锁类型的判断 对于原子类型的原子操作一般是由原子指令直接实现的但是对于部分类型的原子操作仅仅靠原子指令可能无法实现这时原子操作会借助编译器和程序库的内部锁实现。为了判断某一原子类型的操作是基于什么实现的C提供了运行时判断方法和编译时判断方法。 运行时判断几乎全部原子类型都包含成员函数t.is_lock_free()如果此类型对象上的所有原子操作都是无锁的返回true否则返回false。 编译时判断C提供了多种方法在编译时判断原子类型是否无锁 使用宏ATOMIC_xxx_LOCK_FREE该宏中的xxx与要判断的标准原子类型相对应其值包含三种 0原子类型一定有锁1部分机器上该类型无锁2原子类型一定无锁 使用静态成员常量std::atomictype::is_always_lock_free原子类型始终为无锁则为 true若它一定或有时为无锁则为 false使用非成员函数std::atomic_is_lock_free( std::atomicT*)原子类型始终为无锁则为 true若它决不或有时为无锁则为 false std::atomic_llong allong{2}; std::cout is_lock_free: allong.is_lock_free() std::endl; std::cout ATOMIC_LLONG_LOCK_FREE: ATOMIC_LLONG_LOCK_FREE std::endl; std::cout is_always_lock_free: allong.is_always_lock_free std::endl; std::cout atomic_is_lock_free: std::atomic_is_lock_free(allong) std::endl; /** is_lock_free: 1 ATOMIC_LLONG_LOCK_FREE: 2 is_always_lock_free: 1 atomic_is_lock_free: 1 */5.2 特定的原子类型及操作 1. std::atomic_flag std::atomic_flag并非是std::atomicT的特例化它是原子布尔类型且保证一定是无锁的。该类型的对象有两种状态 truefalse 特点 与std::atomicT类似std::atomic_flag也不支持拷贝构造和拷贝赋值因为std::atomic_flag一定是无锁的所以它也不提供is_lock_free()成员函数 创建对象 std::atomic_flag flag;默认初始化。C20之前其状态是未指定的C20起状态被初始化为falsestd::atomic_flag flag ATOMIC_FLAG_INIT;对于C20前可以使用该宏将状态初始化为false 操作 flag.clear()将状态原子地改为falseflag.test_and_set()将状态原子地改为true并返回之前的状态 C20起还添加了获取状态的函数和**与std::atomicT**相同的阻塞唤醒函数 flag.test()原子地返回值flag.wait(old)、falg.notify_one()、flag.notify_all() 自旋锁在获取锁时不阻塞线程通过循环不断尝试获取锁直至成功。利用std::atomic_flag可以简单完美的实现自旋锁。 class Spinlock {public:Spinlock() : flag_(ATOMIC_FLAG_INIT) {}void Lock() {while (flag_.test_and_set());}void UnLock() {flag_.clear();}private:std::atomic_flag flag_; };Spinlock sp; int cnt 0;void AddCnt() {for (int i 0; i 1000000; i) {// 利用 atomic_flag 实现的自旋锁实现互斥sp.Lock();cnt;sp.UnLock();} } 自旋锁可以充分利用CPU但是会造成性能的浪费。如果线程只是短时间阻塞自旋锁非常高效。通常会将自旋锁与互斥量结合使用需要阻塞等待时先在短时间内利用自旋锁等待当超过一定时间后转换为利用互斥量休眠等待。 std::atomicbool是标准的bool原子类型相比于std::atomic_flag它可以通过赋值和store设置目标值可以通过load和类型转换运算符读取值exchange和std::atomic_flag::test_and_set()也非常类似。std::atomicbool相比于std::atomic_flag更加的灵活其缺点是并不保证所有实现都是无锁的。 2. 原子指针和原子整数 std::atomicT*是原子化的指针std::atomicint、std::atomiclong等是原子化的整数它们除了支持主模板定义的操作外还特例化提供了原子化的算术运算操作。包含 原子自增/自减运算符operator、operator--等原子加减函数返回加减前的值 fetch_add(n)原子化的加nfetch_sub(n)原子化的减n 原子复合赋值运算符返回运算后的值不是左侧对象的引用 operatoroperator- 此外原子整数还定义了原子位运算操作 原子位运算函数返回运算前的值fetch_and、fetch_or、fetch_xor复合位运算以值返回运算后的结果operator、operator|、operator^ std::atomic_int acnt{0}; int cnt{0};std::vectorstd::thread pool;for (int i 0; i 10; i) {pool.push_back(std::thread([]() {for (int i 0; i 100000; i) {acnt;cnt;}})); } for (int i 0; i 10; i) {pool[i].join(); } std::cout acnt: acnt std::endl; std::cout cnt: cnt std::endl; /** acnt: 1000000 cnt: 752130 */3. std::atomic的泛化 可以给std::atomic传入自定义类型作为模板实参从而创建一个原子化的自定义类型。只有自定义类型是可平凡复制时才能创建该类型的原子类型。 可平凡复制可以将对象按字节复制到char数组或其他对象中并保有其原值。 对于原子化的自定义类型支持主模板定义的初始化、值的存储与获取、比较交换操作等。 与所有的原子类型相同即使自定义类型重载了比较运算符原子化的自定义类型在执行比较交换操作时也是通过逐字节比较对象。 4. 原子化的智能指针 C20开始在头文件memory中通过部分特例化std::atomic定义了原子化的shared_ptrT和weak_ptrT std::atomicstd::shared_ptrTstd::atomicstd::weak_ptrT 它们支持主模板定义的各种初始化、值的存储与获取、比较交换操作等。 5. 非成员函数实现的原子操作 以上各种原子操作都以成员函数的方式使用为了更广的兼容性C还定义了对应于这些成员函数操作的非成员函数。一般情况下这些操作以atomic_作为成员函数名的前缀构成非成员函数版本的名字。 这些成员函数第一个参数是原子类型的指针指向要操作的原子类型。 5.3 内存次序 在单线程中编译器可能会对一段代码的执行顺序进行重排列即代码的实际执行顺序可能与代码顺序不同。如下所示的代码 int x 0, y 0; {x;y; }实际执行时可能会先执行y后执行x C的优化会保证同样的代码在单一线程下的执行一定会得到相同的结果。但是在多线程环境下可能会因为指令重排造成问题。比如多线程环境下在上面的代码中在没有对指令的重排进行限制时当前线程首先执行了y此时如果在另外一个线程中先获取y的值再获取x的值会出现y1, x0的状态这与代码的实际顺序不符。 为了保证代码可以按照我们想要的顺序执行可以限制编译器及CPU对一个线程中指令的重新排列从而避免因重排执行顺序导致代码执行结果与预期不符。如限制y一定在x后执行这样就保证了在多线程环境下也能获取到和代码顺序相符的结果。 对于原子类型的每种操作都可以在所有参数后提供一个额外的std::memory_order类型的内存序参数用于限制编译器在当前线程中将该操作重排到某些位置。 注意所有的内存序都只作用于指定内存序的操作所运行的线程不会跨线程交换指令执行的顺序之所以能够利用内存序保证多个线程中原子操作的执行顺序是因为利用了如while循环等的手段等待另一个线程中的某个操作A执行完成如果在那个线程中限制了A操作前的指令不能重排到A后那么在当前线程while循环结束后表示A执行完成则当前线程一定能正确获得A之前指令的操作结果。 std::memory_order包含六种按照对指令重排的限制程度从强到弱可以分为四类 顺序一致次序Sequentially-consistent ordering std::memory_order_seq_cst 释放-获取次序Release-Acquire ordering std::memory_order_releasestd::memory_order_acquirestd::memory_order_acq_rel 释放-消费次序Release-Consume ordering std::memory_order_consume 宽松次序Relaxed ordering std::memory_order_relaxed 1. 顺序一致次序 顺序一致次序是原子操作的默认内存序。即当调用某个原子操作且没有指定任何内存序参数时默认使用的就是std::memory_order_seq_cst。 顺序一致性规定 同一线程内的多个顺序一致的操作会按照代码顺序执行一个顺序一致的操作结束后操作结果会对后续代码和其他线程立即可见操作执行结束后会从缓存立即同步到所有使用它的地方避免像relaxed序一样可能会延迟同步结果 顺序一致次序是最强的约束它可以保证程序的运行严格按照代码顺序执行但是在部分机器上为了实现该约束可能会造成较大的性能损失。 // 虽然原子操作内存序的默认参数就是 std::memory_order_seq_cst // 但是以下代码仍然显式指定了 std::memory_order_seq_cst 以突出显式 #include atomic #include cassert #include iostream #include thread/** brief 先修改 x再修改 y */ void WriteXAndY() {x.store(true, std::memory_order_seq_cst);y.store(true, std::memory_order_seq_cst); }/** brief 先读取 x再读取 y */ void ReadXThenY() {while (!x.load(std::memory_order_seq_cst));if (y.load(std::memory_order_seq_cst)) {std::cout 111 std::endl;z;} } /** brief 先读取 y再读取 x */ void ReadYThenX() {while (!y.load(std::memory_order_seq_cst));if (x.load(std::memory_order_seq_cst)) {z;std::cout 222 std::endl;} }std::atomicbool x, y; std::atomicint z;int main() {x false;y false;z 0;std::thread c(ReadXThenY);std::thread d(ReadYThenX);std::thread e(WriteXAndY);e.join();c.join();d.join();return 0; } 以上代码中无论编译器或CPU对指令怎么重排按照内存序的约束ReadYThenX一定会执行。即当y被修改为true时x一定也已经被修改为true了。但是ReadXThenY不一定执行因为按照代码顺序执行完x.store(true, std::memory_order_seq_cst)后可能会发生进程的调度造成不能进入ReadXThenY的if判断内。 2. 宽松次序 宽松次序规定在一个线程内 对同一原子变量的操作按照代码顺序执行操作结果对其他线程可能会延迟可见最终一定会可见对不同原子变量的操作可以重排顺序 宽松次序是最松的约束指定宽松次序后允许编译器根据需要重排指令的顺序以代码提高运行效率。 #include atomic #include iostream #include threadstd::atomicint x(0), y(1); int r1 2, r2 3; void WriteX() {r1 y.load(std::memory_order_relaxed);x.store(r1, std::memory_order_relaxed); }void WriteY() {r2 x.load(std::memory_order_relaxed);y.store(42, std::memory_order_relaxed); }int main(int argc, char const *argv[]) {std::thread a(WriteX);std::thread b(WriteY);a.join();b.join();return 0; }以上原子操作都指定了std::memory_order_relaxed编译器如果对执行指令重排再叠加进程调度最终的执行结果可能会出现r1 42, r2 42的情况。即重排后先执行y.store指令重排、再执行r1 y.load进程调度、再执行x.store(r1)、最后执行r2 x.load进程调度指令重排 宽松次序最常见的应用是计数器自增 #include iostream #include thread #include atomic #include vectorstd::atomicint cnt(0);/** brief 对 cnt 递增 100 次 */ void Work() {for (int i 0; i 100; i) {cnt.fetch_add(1, std::memory_order_relaxed);} }int main() {std::vectorstd::thread ts;for (int i 0; i 10; i) {ts.emplace_back(Work);}for (int i 0; i 10; i) {ts[i].join();}std::cout cnt: cnt.load() std::endl; } /* cnt: 1000 */ 因为计数器自增只要求原子性对执行的顺序并不敏感且不会影响与其他线程的同步即使编译器为了优化性能发生了指令的重排对最终的结果也不会造成影响 3. 释放-获取次序 释放获取次序主要包含两个它们都是只影响操作所在的线程 std::memory_order_release用来修饰一个写操作如store限制在该写操作前的所有操作包含非原子、原子及宽松原子的读写不能重排到该写操作之后且如果有操作发生了内存写入写入的结果会在运行完该写操作后在其他线程立即可见std::memory_order_acquire用来修饰一个读操作如load限制在该读操作后的所有操作包含非原子、原子及宽松原子的读写不能重排到该读操作前 此外还有一个同时作用获取释放的次序 std::memory_order_acq_rel修饰一个读-改-写操作如exchange同时包含上面两个修饰符的作用 从定义可以看出写操作的释放保证一个线程中该写操作之前的代码一定执行完成读操作的获取保证一个线程中该读操作后的所有代码都在读操作后执行。因此可以利用该特性实现内存操作的同步。 #include atomic #include iostream #include threadstd::atomicbool x false, y false; int data 0;void Producer() {x.store(true, std::memory_order_relaxed);data 42;// memory_order_release 声明之前的所有代码不能重排到后面y.store(true, std::memory_order_release); }void Consumer() {while (!y.load(std::memory_order_acquire));// memory_order_acquire 声明之后的代码不能重排到前面if (x.load() true data 42) {std::cout Yep\n;} }int main(int argc, char const *argv[]) {std::thread a(Producer);std::thread b(Consumer);a.join();b.join();return 0; }在Consumer中由于循环等待y.load返回true当退出while循环时说明Producer中y.store(true, std::memory_order_release)一定执行完成因为有参数memory_order_release所以y.store前的所有代码也一定执行完成即使x.store指定了memory_order_relaxed。此时在Consumer的while循环后的代码一定能够看到Producer的y.store前的修改所以会输出Yep 实际上顺序一致次序等价于同时指定了释放-获取次序即 对写操作的std::memory_order_seq_cst就是执行std::memory_order_release内存序对读操作的std::memory_order_seq_cst就是执行std::memory_order_acquire内存序 4. 释放-消费操作 释放-消费操作与释放获取操作类似区别在于使用时将std::memory_order_acquire替换为std::memory_order_consume std::memory_order_consume作用于某个原子变量的读操作如load限制当前线程中该原子变量及依赖该原子变量的所有操作不能重排到该读操作之前 代码 std::atomicint * global_addr{nullptr}; void Func(int *data) {int *addr global_addr.load(std::memory_order_consume);int d *data;int f *(data 1);if (addr) {int x *addr;} }由于global_addr的读操作指定了std::memory_order_consume所以依赖于global_addr的addr和x的相关操作不能重排到global_addr.load前。d和f的相关代码不依赖global_addr所以编译器可以将这些代码重排到合适的位置。 将std::memory_order_release和std::memory_order_consume结合可以实现不同线程中只同步相互依赖的变量。如 #include atomic #include iostream #include thread #include stringstd::atomicbool abool false; int data;void Producer() {data 42;abool.store(true, std::memory_order_release);// 保证 abool.store 之前的所有操作都执行完成 } void Consumer() {// 只保证与 abool 相关的操作不会重排到前面while (!abool.load(std::memory_order_consume));if (abool.load() true) {std::cout abool: Yep\n;}// data 可以被重排到 while 前可能会输出 date: Noif (data 42) {std::cout data: Yep\n;} else {std::cout date: No\n;} }int main(int argc, char const *argv[]) {std::thread a(Producer);std::thread b(Consumer);a.join();b.join();return 0; }只有abool的值会在两个线程间同步在Consumer中如果重排data的判断语句到abool.load前会输出date: No 5.4 栅栏 以上的内存序都局限于某个原子操作C11还定义了可以独立于原子操作使用的内存栅栏相比于原子操作的内存序使用栅栏可以强制施加内存次序实现更强的同步效果。 内存栅栏主要通过全局函数设置 void atomic_thread_fence(std::memory_order order) 当线程运行到栅栏函数处时栅栏会对线程中其他原子操作的重排施加限制从而使得这些操作满足特定的执行顺序。 1. 内存栅栏的逻辑 当六种不同的内存序做参数时可以将栅栏类型分为三种 release fence用于阻止当前线程中**fence前的内存操作重排到fence后的任意store之后 **store可以是任意内存序。原子操作的release只限制了之前的操作不能重排到release后release fence则限制不能排到release后的任意store后添加了store条件包括 std::atomic_thread_fence(std::memory_order_release) 根据以上原则以下两种代码有相同的效果 // 代码 1 std::string* p new std::string(Hello); ptr.store(p, std::memory_order_release);// 代码 2 std::string* p new std::string(Hello); std::atomic_thread_fence(memory_order_release); // 由于release fence的限制即使ptr.store是releaxed的也会在p的初始化后执行 ptr.store(p, std::memory_order_relaxed); acquire fence用于阻止当前线程中**fence**后的内存操作重排到**fence前的任意load之前**load可以是任意内存序。原子操作的acquire只限制了之后的操作不能重排到acquire前acquire fence则限制不能排到release前的任意load前添加了load条件包括 std::atomic_thread_fence(std::memory_order_acquire)std::atomic_thread_fence(std::memory_order_consume) 根据以上原则以下两种代码有相同的效果 // 代码 1 std::string* p; while(p ptr.load(std::memory_order_acquire)); assert(*p hello)// 代码 2 std::string* p; // 由于acquire fence的限制即使ptr.load是relaxed的也会在*p前执行 while(p ptr.load(std::memory_order_relaxed)); std::atomic_thread_fence(memory_order_acquire); assert(*p hello) full fencerelease fence和acquire fence的组合可以同时实现以上两者的功能 std::atomic_thread_fence(std::memory_order_acq_rel)release fence和acquire fence的组合std::atomic_thread_fence(std::memory_order_seq_cst)额外保证有完全顺序一致性的full fence具有最强的约束 std::atomic_thread_fence(std::memory_order_relaxed)类型的栅栏不限制任何重排 2. 内存栅栏的使用 根据以上栅栏内存序通过和原子内存次序的组合可以形成三种同步。 1. release fence-acquire atomic同步 如下在两个线程运行的代码 std::atomicbool atm;// Thread A void WorkA() {// ops1std::atomic_thread_fence(std::memory_order_release);atm.store(true, std::memory_order_relaxed); }// Thread B void WorkB() {while (!atm.load(std::memory_order_acquire));// ops2 }可以构成如下执行流程 Thread AThread Bops1Frelease fenceYwhile(!atm.load(acquire))Xatm.store(any_order)ops2 由于release fence操作限制ops1重排到X后面atomic acquire限制ops2重排到Y前面所以当Y执行完毕时表示F前的所有操作一定执行完成了。因此该release fence与acquire atomic使得两个线程中的ops1部分的代码一定早于ops2部分的代码执行根据上面的规则X的操作只要是store操作即可内存序可以任意 2. release atomic-acquire fence同步 如下在两个线程运行的代码 std::atomicbool atm;// Thread A void WorkA() {// ops1atm.store(true, std::memory_order_release); }// Thread B void WorkB() {while (!atm.load(std::memory_order_relaxed));std::atomic_thread_fence(std::memory_order_acquire);// ops2 }可以构成如下执行流程 Thread AThread Bops1Ywhile(!atm.load(any_order))Xatm.store(release)Facquire fenceops2 由于acquire fence限制ops2重排到Y之前atomic release限制ops1重排到X后面当Y执行完毕时X一定执行完成。因此release atomic与acquire fence使得ops1的代码一定比ops2的代码先执行根据上面的规则Y的操作只要是load操作即可内存序可以任意 3. release fence-acquire fence同步 如下两个线程运行的代码 std::atomicbool atm;// Thread A void WorkA() {// ops1std::atomic_thread_fence(std::memory_order_release);atm.store(true, std::memory_order_relaxed); } // Thread B void WorkB() {while (!atm.load(std::memory_order_relaxed));std::atomic_thread_fence(std::memory_order_acquire);// ops2 }可以构成如下执行流程 Thread AThread Bops1Ywhile(!var.load(any_order))FArelease fenceFBacquire fenceXvar.store(any_order)ops2 由于release fence限制ops1必须在X之前执行acquire fence限制ops2必须在Y之后执行。当Y执行完成后表示X一定执行过了。因此release fence与acquire fence使得ops1的代码一定早于ops2执行 参考资料 C11中的内存模型上篇 - 内存模型基础如何理解 C11 的六种 memory orderc并发编程1.内存序C11中的内存模型下篇 - C11支持的几种内存模型C memory order循序渐进四—— 在std::atomic_thread_fence 上应用std::memory_order实现不同的内存序内存模型与c中的memory order
http://www.hkea.cn/news/14289791/

相关文章:

  • 网站建设流程html产品运营主要做什么
  • 国内wordpress主题网站自学网站建设要看什么书
  • 地产平面网站九度互联网站制作效果
  • 襄阳网站建设公司招聘房子装修设计网
  • 吉林网站建设曲靖市麒麟区建设局规划网站
  • 网站建设瀑布流织梦下载网站模板
  • 网站开发标书昆明网站建设网站
  • 起名字最好的网站欧洲服务器免费ip地址
  • 无锡上网站建设网站后期维护价格
  • 网站seo优化皆宣徐州百都网络不错做网站都有跳转链接
  • 买高端品牌网站建设潍坊高密网站建设
  • 做彩网站三室一厅装修效果图
  • 网站的建设目标有哪些产品开发的流程
  • 做电影网站算侵权吗做本地团购网站
  • 网站的源代码有什么用个人承包工程需要什么资质
  • 梧州网站推广费用淮南网备案查询
  • 临翔网站建设有哪几个网站可以做贸易
  • 网红网站建设五金配件店 东莞网站建设
  • wap网站推广方法商标注册网站缴费入口
  • 百度收录网站方法自己创业网站开发
  • 微同步网站手机网站怎么搭建
  • 不懂编程如何做网站产品画册
  • 大莲网站建设公司优化设计三年级上册答案语文
  • 关于官方网站建设情况的汇报哪做网站好
  • 做免费外贸网站申请账号注册
  • 电子商务网站与建设实践报告塘沽网红餐厅
  • 浙江新地标建设集团网站口碑营销案例简短
  • 惠州市惠城区建设局网站外贸网址
  • 无锡手机网站建设品牌网站建设4小蝌蚪
  • 免费建小程序网站快照打开是网站网站