C++11多线程 —— 原子类型及其操作

所在头文件:<atomic>

1. 原子类型

The only type that doesn’t provide an is_lock_free() member function is std::atomic_flag. This type is a really simple Boolean flag, and operations on this type are required to be lock-free.

The remaining atomic types are all accessed through specializations of the std::atomic<> class template and are a bit more full-featured but may not be lock-free.

在这里插入图片描述
在这里插入图片描述

原子类型的对象不可复制、不可赋值,但支持隐式转换到对应的非原子类型,也支持从非原子类型的对象来构造原子类型对象)The standard atomic types are not copyable or assignable in the conventional sense, in that they have no copy constructors or copy assignment operators. They do, however, support assignment from and implicit conversion to the corresponding built-in types as well as direct load() and store() member functions, exchange(), compare_exchange_weak(), and compare_exchange_strong(). They also support the compound assignment operators where appropriate: +=, -=, *=, |=, and so on, and the integral types and std::atomic<> specializations for pointers support ++ and --.

命名函数和操作符的区别)These operators also have corresponding named member functions with the same functionality: fetch_add(), fetch_or(), and so on. The return value from the assignment operators and member functions is either the value stored (in the case of the assignment operators) or the value prior to the operation (in the case of the named functions).

可以指定操作的内存序)Each of the operations on the atomic types has an optional memory-ordering argument that can be used to specify the required memory-ordering semantics.

  • Store operations, which can have memory_order_relaxed, memory_order_release,
    or memory_order_seq_cst ordering
  • Load operations, which can have memory_order_relaxed, memory_order_consume,
    memory_order_acquire, or memory_order_seq_cst ordering
  • Read-modify-write operations, which can have memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, or memory_order_seq_cst ordering

The default ordering for all operations is memory_order_seq_cst.


2. std::atomic_flag 上的操作

  • 两种状态)Objects of this type can be in one of two states: set or clear.

  • 初始化)Objects of type std::atomic_flag must be initialized with ATOMIC_FLAG_INIT. This initializes the flag to a clear state.

    std::atomic_flag f=ATOMIC_FLAG_INIT;
    
  • 如果是static的,则尽可放心)If the std::atomic_flag object has static storage duration, it’s guaranteed to be statically initialized, which means that there are no initialization order issues; it will always be initialized by the time of the first operation on the flag.

  • 三种操作)Once you have your flag object initialized, there are only three things you can do
    with it: destroy it, clear it, or set it and query the previous value. These correspond to the destructor, the clear() member function, and the test_and_set() member function, respectively. Both the clear() and test_and_set() member functions can have a memory order specified. clear() is a store operation and so can’t have memory_order_acquire or memory_order_acq_rel semantics, but test_and_set() is a read-modify-write operation and so can have any of the memory-ordering tags applied.

    f.clear(std::memory_order_release);		//	让其进入 clear 状态
    bool x=f.test_and_set();				// 返回旧值,并设置它
    
  • 使用例子:实现自旋锁

    class spinlock_mutex {
    private:
    	std::atomic_flag flag;
    	
    public:
    	spinlock_mutex():flag(ATOMIC_FLAG_INIT) {}
    	
    	void lock() {
    		while(flag.test_and_set(std::memory_order_acquire));
    	}
    	
    	void unlock() {
    		flag.clear(std::memory_order_release);
    	}
    };
    

    Initially the flag is clear and the mutex is unlocked. To lock the mutex, loop on test_and_set() until the old value is false, indicating that this thread set the value to true. Unlocking the mutex is simply a matter of clearing the flag.


3. std::atomic<bool> 上的操作

  • This is a more full-featured Boolean flag than std::atomic_flag, as you might expect. Although it’s still not copy-constructible or copy-assignable, you can construct it from a nonatomic bool, so it can be initially true or false, and you can also assign to instances of std::atomic<bool> from a nonatomic bool. 如,

    std::atomic<bool> b(true);
    b=false;
    
  • 返回一个值,而非一个引用!)A common pattern with the atomic types: the assignment operators they support return values (of the corresponding nonatomic type) rather than references.

  • 操作

    • 写操作: writes (of either true or false) are done by calling store(), and the memory-order semantics can still be specified.

    • 读操作:std::atomic<bool> also supports a plain nonmodifying query of the value with an implicit conversion to plain bool or with an explicit call to load().

    • 获取旧值并设置新值exchange() member function that allows you to replace the stored value with a new one of your choosing and atomically retrieve the original value.

      std::atomic<bool> b;
      bool x=b.load(std::memory_order_acquire);
      b.store(true);
      x=b.exchange(false,std::memory_order_acq_rel);
      
    • 根据当前值,设置或不设置一个新值
      compare_exchange_weak() and compare_exchange_strong() member functions.

      工作过程It compares the value of the atomic variable with a supplied expected value and stores the supplied desired value if they’re equal. If the values aren’t equal, the expected value is updated with the actual value of the atomic variable. The return type of the compare/exchange functions is a bool, which is true if the store was performed and false otherwise.

      weak为什么weak?For compare_exchange_weak(), the store might not be successful even if the original value was equal to the expected value, in which case the value of the variable is unchanged and the return value of compare_exchange_weak() is false. This is most likely to happen on machines that lack a single compare-and-exchange instruction.

      Because compare_exchange_weak() can fail spuriously, it must typically be used in a loop:

      bool expected=false;
      extern atomic<bool> b; // set somewhere else
      
      while(!b.compare_exchange_weak(expected,true) && !expected);
      

      解释:b中的bool值和expected的值进行比较,如果两者相等,则将b中的bool值设为true,否则,将expected的值更新为b中的bool值。

      strong为什么strong?)On the other hand, compare_exchange_strong() is guaranteed to return false only if the actual value wasn’t equal to the expected value. This can eliminate the need for loops like the one shown.

      可以带两个内存序参数)The compare/exchange functions are also unusual in that they can take two memory-ordering parameters. This allows for the memory-ordering semantics to differ in the case of success and failure; it might be desirable for a successful call to have memory_order_acq_rel semantics whereas a failed call has memory_order_relaxed semantics.

      • A failed compare/exchange doesn’t do a store, so it can’t have memory_order_release or memory_order_acq_rel semantics.

      • You also can’t supply stricter memory ordering for failure than for success; if you want memory_order_acquire or memory_order_seq_cst semantics for failure, you must specify those for success as well.

      • If you don’t specify an ordering for failure, it’s assumed to be the same as that for success, except that the release part of the ordering is stripped: memory_order_release becomes memory_order_relaxed, and memory_order_acq_rel becomes memory_order_acquire.

      • If you specify neither, they default to memory_order_seq_cst as usual.
        如:以下两种方式是等价的

        std::atomic<bool> b;
        bool expected = false;
        b.compare_exchange_weak(expected,true, memory_order_acq_rel,memory_order_acquire);
        b.compare_exchange_weak(expected,true,memory_order_acq_rel);
        

4. std::atomic<T*> 上的操作

The atomic form of a pointer to some type T is std::atomic<T*>.

接口是相同的)The interface is essentially the same as std::atomic<bool>, although it operates on values of the corresponding pointer type rather than bool values.

It can be both constructed and assigned from the suitable pointer values.

新的操作:算术运算)The new operations provided by std::atomic<T*> are the pointer arithmetic operations. The basic operations are provided by the fetch_add() and fetch_sub() member functions, which do atomic addition and subtraction on the stored address, and the operators += and -=, and both pre- and post-increment and decrement with ++ and - -.

注意:函数版本的操作的返回值是该原子变量的旧值,而操作符版本的操作的返回值是计算之后的结果。而且,它们从不返回引用,而是返回一个值。这些和整数类型的原子变量也是一样的。

如,

class Foo{};
Foo some_array[5];

std::atomic<Foo*> p(some_array);

Foo* x=p.fetch_add(2);			// Add 2 to p and return old value

x=(p-=1);						// Subtract 1 from p and return new value

The function forms also allow the memory-ordering semantics to be specified as an additional function call argument:

p.fetch_add(3,std::memory_order_release);

both fetch_add() and fetch_sub() are read-modify-write operations.

操作符版本的操作的内存序永远是 memory_order_seq_cst.


5. 整数类型的原子变量上的操作

提供了很多算术运算操作)As well as the usual set of operations (load(), store(), exchange(), compare_exchange_weak(), and compare_exchange_strong()), the atomic integral types such as std::atomic<int> or std::atomic<unsigned long long> have quite a comprehensive set of operations available: fetch_add(), fetch_sub(), fetch_and(), fetch_or(), fetch_xor(), compound-assignment forms of these operations (+=, -=, &=, |=, and ^=), and pre- and post-increment and decrement (++x, x++, --x, and x–). It’s not quite the full set of compound-assignment operations you could do on a normal integral type, but it’s close enough: only division, multiplication, and shift operators are missing.

The named functions atomically perform their operation and return the old value, whereas the compound-assignment operators return the new value.


6. std::atomic<> 类模板

允许定义用户自定义类型)The presence of the primary template allows a user to create an atomic variant of a user-defined type, in addition to the standard atomic types.

用户自定义类型需满足的标准

  • In order to use std::atomic<UDT> for some user-defined type UDT, this type must have a trivial copy-assignment operator. This means that the type must not have any virtual functions or virtual base classes and must use the compiler-generated copy-assignment operator.
  • Not only that, but every base class and non-static data member of a user-defined type must also have a trivial copy-assignment operator. This essentially permits the compiler to use memcpy() or an equivalent operation for assignment operations, because there’s no user-written code to run.
  • Finally, the type must be bitwise equality comparable. You must be able to compare instances for equality using memcmp() . This guarantee is required in order for compare/exchange operations to work.

浮点数)Note that although you can use std::atomic<float> or std::atomic<double>, because the built-in floating point types do satisfy the criteria for use with memcpy and memcmp, the behavior may be surprising in the case of compare_exchange_strong. The operation may fail even though the old stored value was equal in value to the comparand, if the stored value had a different representation. Note that there are no atomic arithmetic operations on floating-point values.


6. 非成员函数版的原子操作

There are also equivalent nonmember functions for all the operations on the various atomic types.

For the most part the nonmember functions are named after the corresponding member functions but with an atomic_ prefix (for example, std::atomic_load()).

指定内存序参数)These functions are then overloaded for each of the atomic types. Where there’s opportunity for specifying a memory-ordering tag, they come in two varieties: one without the tag and one with an _explicit suffix and an additional parameter or parameters for the memory-ordering tag or tags (for example, std::atomic_store(&atomic_var,new_value) versus std::atomic_store_explicit(&atomic_var,new_value,std::memory_order_release).

第一个参数)All the free functions take a pointer to the atomic object as the first parameter.

对智能指针的支持)The C++ Standard Library also provides free functions for accessing instances of std::shared_ptr<> in an atomic fashion.
The atomic operations available are load, store, exchange, and compare/exchange, which are provided as overloads of the same operations on the standard atomic types, taking a std::shared_ptr<>* as the first argument:

std::shared_ptr<my_data> p;

void process_global_data() {
	std::shared_ptr<my_data> local=std::atomic_load(&p);
	process_data(local);
}
void update_global_data() {
	std::shared_ptr<my_data> local(new my_data);
	std::atomic_store(&p,local);
}

支持指定内存序)As with the atomic operations on other types, the _explicit variants are also provided to allow you to specify the desired memory ordering.

猜你喜欢

转载自blog.csdn.net/fcku_88/article/details/88526549