C++11中的使用
atomic是C++标准程序库中的一个头文件,定义了C++11标准中的一些表示线程、并发控制时原子操作的类与方法等。此头文件主要声明了两大类原子对象:std::atomic和std::atomic_flag,另外还声明了一套C风格的原子类型和与C兼容的原子操作的函数。在多线程并发执行时,原子操作是线程不会被打断的执行片段。一些程序设计更为注重性能和效率,需要开发lock-free的算法和数据结构,这就需要更为底层的原子操作与原子类型。原子类型对象的主要特点就是从不同线程并发访问是良性(well-defined)行为,不会导致竞争危害。与之相反,不做适当控制就并发访问非原子对象则会导致未定义(undifined)行为。
atomic_flag类:是一种简单的原子布尔类型,只支持两种操作:test_and_set(flag=true)和clear(flag=false)。跟std::atomic的其它所有特化类不同,它是锁无关的。结合std::atomic_flag::test_and_set()和std::atomic_flag::clear(),std::atomic_flag对象可以当作一个简单的自旋锁(spin lock)使用。atomic_flag只有默认构造函数,禁用拷贝构造函数,移动构造函数实际上也禁用。如果在初始化时没有明确使用宏ATOMIC_FLAG_INIT初始化,那么新创建的std::atomic_flag对象的状态是未指定的(unspecified),既没有被set也没有被clear;如果使用该宏初始化,该std::atomic_flag对象在创建时处于clear状态。
(1)、test_and_set:返回该std::atomic_flag对象当前状态,检查flag是否被设置,若被设置直接返回true,若没有设置则设置flag为true后再返回false。该函数是原子的。
(2)、clear:清除std::atomic_flag对象的标志位,即设置atomic_flag的值为false。
std::atomic类模板:std::atomic比std::atomic_flag功能更加完善。c++11标准库std::atomic提供了针对bool类型、整形(integral)和指针类型的特化实现。每个std::atomic模板的实例化和完全特化定义一个原子类型。若一个线程写入原子对象,同时另一个线程从它读取,则行为良好定义。而且,对原子对象的访问可以按std::memory_order所指定建立线程间同步,并排序非原子的内存访问。std::atomic可以以任何可平凡复制(Trivially Copyable)的类型T实例化。std::atomic既不可复制亦不可移动。
除了std::atomic和std::atomic_flag外,
与原子对象初始化相关的两个宏:
(1)、ATOMIC_VAR_INIT(val):初始化std::atomic对象。This macro expands to a token sequence suitable to initialize an atomic object (of static storage duration) with a value of val. This macro exists for compatibility with C implementations, in which it is used as a constructor-like function for(default-constructed) atomic objects; In C++, this initialization may be performed directly by the initialization constructor.
(2)、ATOMIC_FLAG_INIT:初始化std::atomic_flag对象。This macro is defined in such a way that it can be used to initialize an object of type atomic_flag to the clear state.
std::atomic:Objects of atomic types contain a value of a particular type (T). The main characteristic of atomic objects is that access to this contained value from different threads cannot cause data races (i.e., doing that is well-defined behavior, with accesses properly sequenced). Generally, for all other objects, the possibility of causing a data race for accessing the same object concurrently qualifies the operation as undefined behavior. Additionally, atomic objects have the ability to synchronize access to other non-atomic objects in their threads by specifying different memory orders.
std::atomic_flag:Atomic flags are boolean atomic objects that support two operations:test-and-set and clear.
下面是从其他文章中copy的
[cpp] view plain copy#include "atomic.hpp"
#include #include #include #include #include namespace atomic { ///////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/atomic/ std::atomic ready(false); // atomic_flag::atomic_flag: Constructs an atomic_flag object // The atomic_flag is in an unspecified state on construction (either set or clear), // unless it is explicitly initialized to ATOMIC_FLAG_INIT. std::atomic_flag winner = ATOMIC_FLAG_INIT; void count1m(int id) { while (!ready) { std::this_thread::yield(); } // wait for the ready signal for (volatile int i = 0; i < 1000000; ++i) {} // go!, count to 1 million if (!winner.test_and_set()) { std::cout << "thread #" << id << " won!\n"; } }; int test_atomic_atomic() { // atomic::atomic: Constructs an atomic object std::vector threads; std::cout << "spawning 10 threads that count to 1 million...\n"; for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(count1m, i)); ready = true; for (auto& th : threads) th.join(); return 0; } ///////////////////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/compare_exchange_weak/ // a simple global linked list: struct Node { int value; Node* next; }; std::atomic list_head(nullptr); void append(int val) { // append an element to the list Node* oldHead = list_head; Node* newNode = new Node{ val, oldHead }; // what follows is equivalent to: list_head = newNode, but in a thread-safe way: while (!list_head.compare_exchange_weak(oldHead, newNode)) newNode->next = oldHead; } int test_atomic_compare_exchange_weak() { // atomic::compare_exchange_weak: Compares the contents of the atomic object's contained value with expected: // -if true, it replaces the contained value with val(like store). // - if false, it replaces expected with the contained value // spawn 10 threads to fill the linked list: std::vector threads; for (int i = 0; i<10; ++i) threads.push_back(std::thread(append, i)); for (auto& th : threads) th.join(); // print contents: for (Node* it = list_head; it != nullptr; it = it->next) std::cout << ' ' << it->value; std::cout << '\n'; // cleanup: Node* it; while (it = list_head) { list_head = it->next; delete it; } return 0; } /////////////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/exchange/ std::atomic winner_(false); void count1m_(int id) { while (!ready) {} // wait for the ready signal for (int i = 0; i<1000000; ++i) {} // go!, count to 1 million if (!winner_.exchange(true)) { std::cout << "thread #" << id << " won!\n"; } }; int test_atomic_exchange() { // atomic::exchange: Replaces the contained value by val and returns the value it had immediately before std::vector threads; std::cout << "spawning 10 threads that count to 1 million...\n"; for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(count1m_, i)); ready = true; for (auto& th : threads) th.join(); return 0; } ///////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/load/ std::atomic foo(0); void set_foo(int x) { foo.store(x, std::memory_order_relaxed); // set value atomically } void print_foo() { int x; do { x = foo.load(std::memory_order_relaxed); // get value atomically } while (x == 0); std::cout << "foo: " << x << '\n'; } int test_atomic_load() { // atomic::load: Returns the contained value. // The operation is atomic and follows the memory ordering specified by sync. std::thread first(print_foo); std::thread second(set_foo, 10); first.join(); second.join(); return 0; } //////////////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/operator=/ std::atomic foo_ = 0; void set_foo_(int x) { foo_ = x; } void print_foo_() { while (foo_ == 0) { // wait while foo_=0 std::this_thread::yield(); } std::cout << "foo_: " << foo_ << '\n'; } int test_atomic_operator() { // atomic::operator=: Replaces the stored value by val. // This operation is atomic and uses sequential consistency (memory_order_seq_cst). // To modify the value with a different memory ordering std::thread first(print_foo_); std::thread second(set_foo_, 10); first.join(); second.join(); return 0; } /////////////////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic/store/ int test_atomic_store() { // atomic::store: Replaces the contained value with val. // The operation is atomic and follows the memory ordering specified by sync. std::thread first(print_foo); std::thread second(set_foo, 10); first.join(); second.join(); return 0; } ///////////////////////////////////////////////////////////////////// // reference: https://www.cplusplus.com/reference/atomic/atomic_flag/clear/ std::atomic_flag lock_stream = ATOMIC_FLAG_INIT; std::stringstream stream; void append_number(int x) { while (lock_stream.test_and_set()) {} stream << "thread #" << x << '\n'; lock_stream.clear(); } int test_atomic_flag_atomic_clear() { // atomic_flag::clear: Clears the atomic_flag (i.e., sets it to false) //Clearing the atomic_flag makes the next call to member atomic_flag::test_and_set on this object return false. // The operation is atomic and follows the memory ordering specified by sync. // atomic_flag::test_and_set: Sets the atomic_flag and returns whether it was already set immediately before the call // The entire operation is atomic (an atomic read-modify-write operation): the value is not affected by other threads // between the instant its value is read (to be returned) and the moment it is modified by this function. std::vector threads; for (int i = 1; i <= 10; ++i) threads.push_back(std::thread(append_number, i)); for (auto& th : threads) th.join(); std::cout << stream.str(); return 0; } } // namespace atomic
下一篇: 女朋友装容太漂亮男朋友不认识