原文地址:https://www.cnblogs.com/haippy/p/3252056.html

C++11 并发指南已经写了 5 章,前五章重点介绍了多线程编程方面的内容,但大部分内容只涉及多线程、互斥量、条件变量和异步编程相关的 API,C++11 程序员完全可以不必知道这些 API 在底层是如何实现的,只需要清楚 C++11 多线程和异步编程相关 API 的语义,然后熟加练习即可应付大部分多线程编码需求。但是在很多极端的场合下为了性能和效率,我们需要开发一些 lock-free 的算法和数据结构,前面几章的内容可能就派不上用场了,因此从本文开始介绍 C++11 标准中 <atomic> 头文件里面的类和相关函数。

本文介绍 <atomic> 头文件中最简单的原子类型: atomic_flag。atomic_flag 一种简单的原子布尔类型,只支持两种操作,test-and-set 和 clear。

std::atomic_flag 构造函数

std::atomic_flag 构造函数如下:

  • atomic_flag() noexcept = default;
  • atomic_flag (const atomic_flag&T) = delete;

std::atomic_flag 只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他的 std::atomic_flag 对象构造一个新的 std::atomic_flag 对象。

如果在初始化时没有明确使用 ATOMIC_FLAG_INIT初始化,那么新创建的 std::atomic_flag 对象的状态是未指定的(unspecified)(既没有被 set 也没有被 clear。)另外,atomic_flag不能被拷贝,也不能 move 赋值。

ATOMIC_FLAG_INIT: 如果某个 std::atomic_flag 对象使用该宏初始化,那么可以保证该 std::atomic_flag 对象在创建时处于 clear 状态。

下面先看一个简单的例子,main() 函数中创建了 10 个线程进行计数,率先完成计数任务的线程输出自己的 ID,后续完成计数任务的线程不会输出自身 ID:

#include <iostream>              // std::cout
#include <atomic>                // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT
#include <thread>                // std::thread, std::this_thread::yield
#include <vector>                // std::vectorstd::atomic<bool> ready(false);    // can be checked without being set
std::atomic_flag winner = ATOMIC_FLAG_INIT;    // always set when checkedvoid count1m(int id)
{while (!ready) {std::this_thread::yield();} // 等待主线程中设置 ready 为 true.for (int i = 0; i < 1000000; ++i) {} // 计数.// 如果某个线程率先执行完上面的计数过程,则输出自己的 ID.// 此后其他线程执行 test_and_set 是 if 语句判断为 false,// 因此不会输出自身 ID.if (!winner.test_and_set()) {std::cout << "thread #" << id << " won!\n";}
};int main()
{std::vector<std::thread> 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;
}

多次执行结果如下:

atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #6 won!
atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #1 won!
atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #5 won!
atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #1 won!
atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #1 won!
atomic ) ./Atomic-Flag1
spawning 10 threads that count to 1 million...
thread #10 won!

std::atomic_flag::test_and_set 介绍

std::atomic_flag 的 test_and_set 函数原型如下:

bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept;
bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;

test_and_set() 函数检查 std::atomic_flag 标志,如果 std::atomic_flag 之前没有被设置过,则设置 std::atomic_flag 的标志,并返回先前该 std::atomic_flag 对象是否被设置过,如果之前 std::atomic_flag 对象已被设置,则返回 true,否则返回 false。

test-and-set 操作是原子的(因此 test-and-set 是原子 read-modify-write (RMW)操作)。

test_and_set 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 test_and_set 参数 sync 的取值),取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

一个简单的例子:

#include <iostream>                // std::cout
#include <atomic>                // std::atomic_flag
#include <thread>                // std::thread
#include <vector>                // std::vector
#include <sstream>                // std::stringstreamstd::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 main()
{std::vector < std::thread > 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() << std::endl;;return 0;
}

执行结果如下:

thread #1
thread #2
thread #3
thread #4
thread #5
thread #6
thread #7
thread #8
thread #9
thread #10

std::atomic_flag::clear() 介绍

清除 std::atomic_flag 对象的标志位,即设置 atomic_flag 的值为 false。clear 函数原型如下:

void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;

清除 std::atomic_flag 标志使得下一次调用 std::atomic_flag::test_and_set 返回 false。

std::atomic_flag::clear() 可以指定 Memory Order(后续的文章会详细介绍 C++11 的 Memory Order,此处为了完整性列出 clear 参数 sync 的取值),取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

结合 std::atomic_flag::test_and_set() 和 std::atomic_flag::clear(),std::atomic_flag 对象可以当作一个简单的自旋锁使用,请看下例:

#include <thread>
#include <vector>
#include <iostream>
#include <atomic>std::atomic_flag lock = ATOMIC_FLAG_INIT;void f(int n)
{for (int cnt = 0; cnt < 100; ++cnt) {while (lock.test_and_set(std::memory_order_acquire))  // acquire lock; // spinstd::cout << "Output from thread " << n << '\n';lock.clear(std::memory_order_release);               // release lock}
}int main()
{std::vector<std::thread> v;for (int n = 0; n < 10; ++n) {v.emplace_back(f, n);}for (auto& t : v) {t.join();}
}

在上面的程序中,std::atomic_flag 对象 lock 的上锁操作可以理解为 lock.test_and_set(std::memory_order_acquire); (此处指定了 Memory Order,更多有关 Memory Order 的概念,我会在后续的文章中介绍),解锁操作相当与 lock.clear(std::memory_order_release)。

在上锁的时候,如果 lock.test_and_set 返回 false,则表示上锁成功(此时 while 不会进入自旋状态),因为此前 lock 的标志位为 false(即没有线程对 lock 进行上锁操作),但调用 test_and_set 后 lock 的标志位为 true,说明某一线程已经成功获得了 lock 锁。

如果在该线程解锁(即调用 lock.clear(std::memory_order_release)) 之前,另外一个线程也调用 lock.test_and_set(std::memory_order_acquire) 试图获得锁,则 test_and_set(std::memory_order_acquire) 返回 true,则 while 进入自旋状态。如果获得锁的线程解锁(即调用了 lock.clear(std::memory_order_release))之后,某个线程试图调用 lock.test_and_set(std::memory_order_acquire) 并且返回 false,则 while 不会进入自旋,此时表明该线程成功地获得了锁。

按照上面的分析,我们知道在某种情况下 std::atomic_flag 对象可以当作一个简单的自旋锁使用。

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)  一文介绍了 C++11 中最简单的原子类型 std::atomic_flag,但是 std::atomic_flag 过于简单,只提供了 test_and_set 和 clear 两个 API,不能满足其他需求(如 store, load, exchange, compare_exchange 等),因此本文将介绍功能更加完善的 std::atomic 类。

std::atomic 基本介绍

std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。

template <class T> struct atomic;

原子类型对象的主要特点就是从不同线程访问不会导致数据竞争(data race)。因此从不同线程访问某个原子对象是良性 (well-defined) 行为,而通常对于非原子类型而言,并发访问某个对象(如果不做任何同步操作)会导致未定义 (undifined) 行为发生。

C++11 标准中的基本 std::atomic 模板定义如下:

template < class T > struct atomic {bool is_lock_free() const volatile;bool is_lock_free() const;void store(T, memory_order = memory_order_seq_cst) volatile;void store(T, memory_order = memory_order_seq_cst);T load(memory_order = memory_order_seq_cst) const volatile;T load(memory_order = memory_order_seq_cst) const;operator  T() const volatile;operator  T() const;T exchange(T, memory_order = memory_order_seq_cst) volatile;T exchange(T, memory_order = memory_order_seq_cst);bool compare_exchange_weak(T &, T, memory_order, memory_order) volatile;bool compare_exchange_weak(T &, T, memory_order, memory_order);bool compare_exchange_strong(T &, T, memory_order, memory_order) volatile;bool compare_exchange_strong(T &, T, memory_order, memory_order);bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst) volatile;bool compare_exchange_weak(T &, T, memory_order = memory_order_seq_cst);bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst) volatile;bool compare_exchange_strong(T &, T, memory_order = memory_order_seq_cst);atomic() = default;constexpr atomic(T);atomic(const atomic &) = delete;atomic & operator=(const atomic &) = delete;atomic & operator=(const atomic &) volatile = delete;T operator=(T) volatile;T operator=(T);
};

另外,C++11 标准库 std::atomic 提供了针对整形(integral)和指针类型的特化实现,分别定义如下:

针对整形(integal)的特化,其中 integal 代表了如下类型char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_t, char32_t, wchar_t:

template <> struct atomic<integral> {

    bool is_lock_free() const volatile;

    bool is_lock_free() const;

    void store(integral, memory_order = memory_order_seq_cst) volatile;

    void store(integral, memory_order = memory_order_seq_cst);

    integral load(memory_order = memory_order_seq_cst) const volatile;

    integral load(memory_order = memory_order_seq_cst) const;

    operator integral() const volatile;

    operator integral() const;

    integral exchange(integral, memory_order = memory_order_seq_cst) volatile;

    integral exchange(integral, memory_order = memory_order_seq_cst);

    bool compare_exchange_weak(integral&, integral, memory_order, memory_order) volatile;

    bool compare_exchange_weak(integral&, integral, memory_order, memory_order);

    bool compare_exchange_strong(integral&, integral, memory_order, memory_order) volatile;

    bool compare_exchange_strong(integral&, integral, memory_order, memory_order);

    bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst) volatile;

    bool compare_exchange_weak(integral&, integral, memory_order = memory_order_seq_cst);

    bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst) volatile;

    bool compare_exchange_strong(integral&, integral, memory_order = memory_order_seq_cst);

    integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile;

    integral fetch_add(integral, memory_order = memory_order_seq_cst);

    integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile;

    integral fetch_sub(integral, memory_order = memory_order_seq_cst);

    integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile;

    integral fetch_and(integral, memory_order = memory_order_seq_cst);

    integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile;

    integral fetch_or(integral, memory_order = memory_order_seq_cst);

    integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile;

    integral fetch_xor(integral, memory_order = memory_order_seq_cst);

    

    atomic() = default;

    constexpr atomic(integral);

    atomic(const atomic&) = delete;

    atomic& operator=(const atomic&) = delete;

    atomic& operator=(const atomic&) volatile = delete;

    

    integral operator=(integral) volatile;

    integral operator=(integral);

    

    integral operator++(int) volatile;

    integral operator++(int);

    integral operator--(int) volatile;

    integral operator--(int);

    integral operator++() volatile;

    integral operator++();

    integral operator--() volatile;

    integral operator--();

    integral operator+=(integral) volatile;

    integral operator+=(integral);

    integral operator-=(integral) volatile;

    integral operator-=(integral);

    integral operator&=(integral) volatile;

    integral operator&=(integral);

    integral operator|=(integral) volatile;

    integral operator|=(integral);

    integral operator^=(integral) volatile;

    integral operator^=(integral);

};

针对指针的特化:

template <class T> struct atomic<T*> {

    bool is_lock_free() const volatile;

    bool is_lock_free() const;

    void store(T*, memory_order = memory_order_seq_cst) volatile;

    void store(T*, memory_order = memory_order_seq_cst);

    T* load(memory_order = memory_order_seq_cst) const volatile;

    T* load(memory_order = memory_order_seq_cst) const;

    operator T*() const volatile;

    operator T*() const;

    T* exchange(T*, memory_order = memory_order_seq_cst) volatile;

    T* exchange(T*, memory_order = memory_order_seq_cst);

    bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile;

    bool compare_exchange_weak(T*&, T*, memory_order, memory_order);

    bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile;

    bool compare_exchange_strong(T*&, T*, memory_order, memory_order);

    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile;

    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst);

    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile;

    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst);

    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;

    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst);

    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile;

    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst);

    atomic() = default;

    constexpr atomic(T*);

    atomic(const atomic&) = delete;

    atomic& operator=(const atomic&) = delete;

    atomic& operator=(const atomic&) volatile = delete;

    T* operator=(T*) volatile;

    T* operator=(T*);

    T* operator++(int) volatile;

    T* operator++(int);

    T* operator--(int) volatile;

    T* operator--(int);

    T* operator++() volatile;

    T* operator++();

    T* operator--() volatile;

    T* operator--();

    T* operator+=(ptrdiff_t) volatile;

    T* operator+=(ptrdiff_t);

    T* operator-=(ptrdiff_t) volatile;

    T* operator-=(ptrdiff_t);

};

std::atomic 成员函数

好了,对 std::atomic 有了一个最基本认识之后我们来看 std::atomic 的成员函数吧。

std::atomic 构造函数

std::atomic 的构造函数如下:

default (1)
          atomic() noexcept = default;
initialization (2)
constexpr atomic (T val) noexcept;
copy [deleted] (3)
          atomic (const atomic&) = delete;
  1. 默认构造函数,由默认构造函数创建的 std::atomic 对象处于未初始化(uninitialized)状态,对处于未初始化(uninitialized)状态 std::atomic对象可以由 atomic_init 函数进行初始化。
  2. 初始化构造函数,由类型 T初始化一个 std::atomic对象。
  3. 拷贝构造函数被禁用。

请看下例:

#include <iostream>       // std::cout

#include <atomic>         // std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT

#include <thread>         // std::thread, std::this_thread::yield

#include <vector>         // std::vector

// 由 false 初始化一个 std::atomic<bool> 类型的原子变量

std::atomic<bool> ready(false);

std::atomic_flag winner = ATOMIC_FLAG_INIT;

void do_count1m(int id)

{

    while (!ready) { std::this_thread::yield(); } // 等待 ready 变为 true.

    for (volatile int i=0; i<1000000; ++i) {} // 计数

    if (!winner.test_and_set()) {

      std::cout << "thread #" << id << " won!\n";

    }

}

int main ()

{

    std::vector<std::thread> 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;

}

std::atomic::operator=() 函数

std::atomic 的赋值操作函数定义如下:

set value (1)
T operator= (T val) noexcept;
T operator= (T val) volatile noexcept;
copy [deleted] (2)
atomic& operator= (const atomic&) = delete;
atomic& operator= (const atomic&) volatile = delete;

可以看出,普通的赋值拷贝操作已经被禁用。但是一个类型为 T 的变量可以赋值给相应的原子类型变量(相当与隐式转换),该操作是原子的,内存序(Memory Order) 默认为顺序一致性(std::memory_order_seq_cst),如果需要指定其他的内存序,需使用 std::atomic::store()。

#include <iostream>             // std::cout

#include <atomic>               // std::atomic

#include <thread>               // std::thread, std::this_thread::yield

std::atomic <int> foo = 0;

void set_foo(int x)

{

    foo = x; // 调用 std::atomic::operator=().

}

void print_foo()

{

    while (foo == 0) { // wait while foo == 0

        std::this_thread::yield();

    }

    std::cout << "foo: " << foo << '\n';

}

int main()

{

    std::thread first(print_foo);

    std::thread second(set_foo, 10);

    first.join();

    second.join();

    return 0;

}

基本 std::atomic 类型操作

本节主要介绍基本 std::atomic 类型所具备的操作(即成员函数)。我们知道 std::atomic 是模板类,一个模板类型为 T 的原子对象中封装了一个类型为 T 的值。本文<std::atomic 基本介绍>一节中也提到了 std::atomic 类模板除了基本类型以外,还针对整形和指针类型做了特化。 特化的 std::atomic 类型支持更多的操作,如 fetch_add, fetch_sub, fetch_and 等。本小节介绍基本 std::atomic 类型所具备的操作:

is_lock_free

bool is_lock_free() const volatile noexcept;

bool is_lock_free() const noexcept;

判断该 std::atomic 对象是否具备 lock-free 的特性。如果某个对象满足 lock-free 特性,在多个线程访问该对象时不会导致线程阻塞。(可能使用某种事务内存transactional memory 方法实现 lock-free 的特性)。

store

void store (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

void store (T val, memory_order sync = memory_order_seq_cst) noexcept;

修改被封装的值,std::atomic::store 函数将类型为 T 的参数 val 复制给原子对象所封装的值。T 是 std::atomic 类模板参数。另外参数 sync 指定内存序(Memory Order),可能的取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_release Release
memory_order_seq_cst Sequentially consistent

请看下面例子:

#include <iostream>       // std::cout

#include <atomic>         // std::atomic, std::memory_order_relaxed

#include <thread>         // std::thread

std::atomic<int> foo(0); // 全局的原子对象 foo

void set_foo(int x)

{

    foo.store(x, std::memory_order_relaxed); // 设置(store) 原子对象 foo 的值

}

void print_foo()

{

    int x;

    do {

        x = foo.load(std::memory_order_relaxed); // 读取(load) 原子对象 foo 的值

    } while (x == 0);

    std::cout << "foo: " << x << '\n';

}

int main ()

{

    std::thread first(print_foo); // 线程 first 打印 foo 的值

    std::thread second(set_foo, 10); // 线程 second 设置 foo 的值

    first.join();

    second.join();

    return 0;

}

load

T load (memory_order sync = memory_order_seq_cst) const volatile noexcept;

T load (memory_order sync = memory_order_seq_cst) const noexcept;

读取被封装的值,参数 sync 设置内存序(Memory Order),可能的取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_seq_cst Sequentially consistent

请看下面例子:

#include <iostream>       // std::cout

#include <atomic>         // std::atomic, std::memory_order_relaxed

#include <thread>         // std::thread

std::atomic<int> foo(0); // 全局的原子对象 foo

void set_foo(int x)

{

    foo.store(x, std::memory_order_relaxed); // 设置(store) 原子对象 foo 的值

}

void print_foo()

{

    int x;

    do {

        x = foo.load(std::memory_order_relaxed); // 读取(load) 原子对象 foo 的值

    } while (x == 0);

    std::cout << "foo: " << x << '\n';

}

int main ()

{

    std::thread first(print_foo); // 线程 first 打印 foo 的值

    std::thread second(set_foo, 10); // 线程 second 设置 foo 的值

    first.join();

    second.join();

    return 0;

}

operator T

operator T() const volatile noexcept;

operator T() const noexcept;

与 load 功能类似,也是读取被封装的值,operator T() 是类型转换(type-cast)操作,默认的内存序是 std::memory_order_seq_cst,如果需要指定其他的内存序,你应该使用 load() 函数。请看下面例子:

#include <iostream>       // std::cout

#include <atomic>         // std::atomic

#include <thread>         // std::thread, std::this_thread::yield

std::atomic<int> foo = 0;

std::atomic<int> bar = 0;

void set_foo(int x)

{

    foo = x;

}

void copy_foo_to_bar()

{

    // 如果 foo == 0,则该线程 yield,

    // 在 foo == 0 时, 实际也是隐含了类型转换操作,

    // 因此也包含了 operator T() const 的调用.

    while (foo == 0) std::this_thread::yield();

    // 实际调用了 operator T() const, 将foo 强制转换成 int 类型,

    // 然后调用 operator=().

    bar = static_cast<int>(foo);

}

void print_bar()

{

    // 如果 bar == 0,则该线程 yield,

    // 在 bar == 0 时, 实际也是隐含了类型转换操作,

    // 因此也包含了 operator T() const 的调用.

    while (bar == 0) std::this_thread::yield();

    std::cout << "bar: " << bar << '\n';

}

int main ()

{

    std::thread first(print_bar);

    std::thread second(set_foo, 10);

    std::thread third(copy_foo_to_bar);

    first.join();

    second.join();

    third.join();

    return 0;

}

exchange

T exchange (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T exchange (T val, memory_order sync = memory_order_seq_cst) noexcept;

读取并修改被封装的值,exchange 会将 val 指定的值替换掉之前该原子对象封装的值,并返回之前该原子对象封装的值,整个过程是原子的(因此exchange 操作也称为 read-modify-write 操作)。sync参数指定内存序(Memory Order),可能的取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

请看下面例子,各个线程计数至 1M,首先完成计数任务的线程打印自己的 ID,

#include <iostream>       // std::cout

#include <atomic>         // std::atomic

#include <thread>         // std::thread

#include <vector>         // std::vector

std::atomic<bool> ready(false);

std::atomic<bool> 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 main ()

{

    std::vector<std::thread> 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;

}

compare_exchange_weak

(1)
bool compare_exchange_weak (T& expected, T val,memory_order sync = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_weak (T& expected, T val,memory_order sync = memory_order_seq_cst) noexcept;
(2)
bool compare_exchange_weak (T& expected, T val,memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_weak (T& expected, T val,memory_order success, memory_order failure) noexcept;

比较并交换被封装的值(weak)与参数 expected 所指定的值是否相等,如果:

  • 相等,则用 val 替换原子对象的旧值。
  • 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。

该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于 expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。

在第(2)种情况下,内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。

注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。

与compare_exchange_strong 不同, weak 版本的 compare-and-exchange 操作允许(spuriously 地)返回 false(即原子对象所封装的值与参数 expected 的物理内容相同,但却仍然返回 false),不过在某些需要循环操作的算法下这是可以接受的,并且在一些平台下 compare_exchange_weak 的性能更好 。如果 compare_exchange_weak 的判断确实发生了伪失败(spurious failures)——即使原子对象所封装的值与参数 expected 的物理内容相同,但判断操作的结果却为 false,compare_exchange_weak函数返回 false,并且参数 expected 的值不会改变。

对于某些不需要采用循环操作的算法而言, 通常采用compare_exchange_strong 更好。另外,该函数的内存序由  sync 参数指定,可选条件如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

请看下面的例子(参考):

#include <iostream>       // std::cout

#include <atomic>         // std::atomic

#include <thread>         // std::thread

#include <vector>         // std::vector

// a simple global linked list:

struct Node { int value; Node* next; };

std::atomic<Node*> list_head(nullptr);

void append(int val)

{

    // append an element to the list

    Node* newNode = new Node{val, list_head};

    // next is the same as: list_head = newNode, but in a thread-safe way:

    while (!list_head.compare_exchange_weak(newNode->next,newNode)) {}

    // (with newNode->next updated accordingly if some other thread just appended another node)

}

int main ()

{

    // spawn 10 threads to fill the linked list:

    std::vector<std::thread> 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;

}

可能的执行结果如下:

9 8 7 6 5 4 3 2 1 0

compare_exchange_strong

(1)
bool compare_exchange_strong (T& expected, T val,memory_order sync = memory_order_seq_cst) volatile noexcept;
bool compare_exchange_strong (T& expected, T val,memory_order sync = memory_order_seq_cst) noexcept;
(2)
bool compare_exchange_strong (T& expected, T val,memory_order success, memory_order failure) volatile noexcept;
bool compare_exchange_strong (T& expected, T val,memory_order success, memory_order failure) noexcept;

比较并交换被封装的值(strong)与参数 expected 所指定的值是否相等,如果:

  • 相等,则用 val 替换原子对象的旧值。
  • 不相等,则用原子对象的旧值替换 expected ,因此调用该函数之后,如果被该原子对象封装的值与参数 expected 所指定的值不相等,expected 中的内容就是原子对象的旧值。

该函数通常会读取原子对象封装的值,如果比较为 true(即原子对象的值等于 expected),则替换原子对象的旧值,但整个操作是原子的,在某个线程读取和修改该原子对象时,另外的线程不能对读取和修改该原子对象。

在第(2)种情况下,内存序(Memory Order)的选择取决于比较操作结果,如果比较结果为 true(即原子对象的值等于 expected),则选择参数 success 指定的内存序,否则选择参数 failure 所指定的内存序。

注意,该函数直接比较原子对象所封装的值与参数 expected 的物理内容,所以某些情况下,对象的比较操作在使用 operator==() 判断时相等,但 compare_exchange_weak 判断时却可能失败,因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3,它们在逻辑上都表示"真",但在物理上两者的表示并不相同)。

与compare_exchange_weak 不同, strong版本的 compare-and-exchange 操作不允许(spuriously 地)返回 false,即原子对象所封装的值与参数 expected 的物理内容相同,比较操作一定会为 true。不过在某些平台下,如果算法本身需要循环操作来做检查, compare_exchange_weak 的性能会更好。

因此对于某些不需要采用循环操作的算法而言, 通常采用compare_exchange_strong 更好。另外,该函数的内存序由  sync 参数指定,可选条件如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

请看下面的例子:

#include <iostream>       // std::cout

#include <atomic>         // std::atomic

#include <thread>         // std::thread

#include <vector>         // std::vector

// a simple global linked list:

struct Node { int value; Node* next; };

std::atomic<Node*> list_head(nullptr);

void append(int val)

{

    // append an element to the list

    Node* newNode = new Node{val, list_head};

    // next is the same as: list_head = newNode, but in a thread-safe way:

    while (!(list_head.compare_exchange_strong(newNode->next, newNode)));

    // (with newNode->next updated accordingly if some other thread just appended another node)

}

int main ()

{

    // spawn 10 threads to fill the linked list:

    std::vector<std::thread> 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;

}

好了,本文花了大量的篇幅介绍 std::atomic 基本类型,下一篇博客我会给大家介绍 C++11 的标准库中std::atomic 针对整形(integral)和指针类型的特化版本做了哪些改进。

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

C++11 并发指南六( <atomic> 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std::atomic 针对整形(integral)和指针类型的特化版本做了哪些改进。

总地来说,C++11 标准库中的 std::atomic 针对整形(integral)和指针类型的特化版本新增了一些算术运算和逻辑运算操作。具体如下:

integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile;

integral fetch_add(integral, memory_order = memory_order_seq_cst);

integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile;

integral fetch_sub(integral, memory_order = memory_order_seq_cst);

integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile;

integral fetch_and(integral, memory_order = memory_order_seq_cst);

integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile;

integral fetch_or(integral, memory_order = memory_order_seq_cst);

integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile;

integral fetch_xor(integral, memory_order = memory_order_seq_cst);

integral operator++(int) volatile;

integral operator++(int);

integral operator--(int) volatile;

integral operator--(int);

integral operator++() volatile;

integral operator++();

integral operator--() volatile;

integral operator--();

integral operator+=(integral) volatile;

integral operator+=(integral);

integral operator-=(integral) volatile;

integral operator-=(integral);

integral operator&=(integral) volatile;

integral operator&=(integral);

integral operator|=(integral) volatile;

integral operator|=(integral);

integral operator^=(integral) volatile;

integral operator^=(integral);

下面我们来简单介绍以上的 std::atomic 特化版本的成员函数。

fetch_add

if T is integral (1)
T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_add (T val, memory_order sync = memory_order_seq_cst) noexcept;
if T is pointer (2)
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_add (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

将原子对象的封装值加 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_add 相当与 std::atomic::operator+=。

fetch_sub

if T is integral (1)
T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_sub (T val, memory_order sync = memory_order_seq_cst) noexcept;
if T is pointer (2)
T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) volatile noexcept;
T fetch_sub (ptrdiff_t val, memory_order sync = memory_order_seq_cst) noexcept;

将原子对象的封装值减 val,并返回原子对象的旧值(适用于整形和指针类型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_sub 相当与 std::atomic::operator-=。

fetch_and

T fetch_and (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_and (T val, memory_order sync = memory_order_seq_cst) noexcept;

将原子对象的封装值按位与 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_add 相当与 std::atomic::operator&=。

fetch_or

T fetch_or (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_or (T val, memory_order sync = memory_order_seq_cst) noexcept;

将原子对象的封装值按位或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_or 相当与 std::atomic::operator|=。

fetch_xor

T fetch_xor (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;

T fetch_xor (T val, memory_order sync = memory_order_seq_cst) noexcept;

将原子对象的封装值按位异或 val,并返回原子对象的旧值(只适用于整型的 std::atomic 特化版本),整个过程是原子的。sync 参数指定内存序:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

另外,如果第二个参数不指定(取默认参数 memory_order_seq_cst),则 fetch_xor 相当与 std::atomic::operator^=。

operator++

pre-increment (1)
T operator++() volatile noexcept;
T operator++() noexcept;
post-increment (2)
T operator++ (int) volatile noexcept;
T operator++ (int) noexcept;

自增运算符重载, 第一种形式 (1) 返回自增后的值(即前缀++),第二种形式(2) 返回自增前的值(即后缀++),适用于整形和指针类型的 std::atomic 特化版本。

operator--

自减运算符重载, 第一种形式 (1) 返回自减后的值(即前缀--),第二种形式(2) 返回自减前的值(即后缀--),适用于整形和指针类型的 std::atomic 特化版本。

atomic::operator (comp. assign.)

复合赋值运算符重载,主要包含以下形式:

if T is integral (1)
T operator+= (T val) volatile noexcept;
T operator+= (T val) noexcept;
T operator-= (T val) volatile noexcept;
T operator-= (T val) noexcept;
T operator&= (T val) volatile noexcept;
T operator&= (T val) noexcept;
T operator|= (T val) volatile noexcept;
T operator|= (T val) noexcept;
T operator^= (T val) volatile noexcept;
T operator^= (T val) noexcept;
if T is pointer (2)
T operator+= (ptrdiff_t val) volatile noexcept;
T operator+= (ptrdiff_t val) noexcept;
T operator-= (ptrdiff_t val) volatile noexcept;
T operator-= (ptrdiff_t val) noexcept;

以上各个 operator 都会有对应的 fetch_* 操作,详细见下表:

操作符 成员函数 支持类型
复合赋值 等价于 整型 指针类型 其他类型
+ atomic::operator+= atomic::fetch_add
- atomic::operator-= atomic::fetch_sub
& atomic::operator&= atomic::fetch_and
| atomic::operator|= atomic::fetch_or
^ atomic::operator^= atomic::fetch_xor

好了,本节先介绍这里,下一节我会介绍 C++11 中 C 风格的原子操作 API。

STL::atomic原子操作相关推荐

  1. go atomic 原子操作

    在 go 语言 string 类型思考 中有说到 -race 竞态检测,多个 goroutine 并发读写同一个变量是会触发.竞态竞争导致的问题是:结果不可控,你也无法预料最终的结果是什么. 比较棘手 ...

  2. 2-2-3-3、Atomic原子操作类详解

    目录 Atomic原子操作类 作用 分类 基本类型 引用类型 数组类型 对象属性原子修改器 原子类型累加器(jdk1.8增加的类) 作用 性能测试 条件>>>>>> ...

  3. C++11开发中的Atomic原子操作

    C++11开发中的Atomic原子操作 Nicol的博客铭 原文  https://taozj.org/2016/09/C-11%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84 ...

  4. Concurrent——Atomic原子操作类

    转载自https://blog.csdn.net/sdlyjzh/article/details/78587777 打算把Concurrent包下面的一些类整理一下.今天先从最简单的开始,Atomic ...

  5. java.util.concurrent.atomic原子操作类包

    2019独角兽企业重金招聘Python工程师标准>>> 这个包里面提供了一组原子变量类.其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当 ...

  6. std::atomic原子操作

    1.原子操作介绍 在多线程编程中,经常使用互斥锁锁住一段代码块,实现线程同步.原子操作可以看成是对变量的互斥锁.比如程序中一个线程读取一个变量,另一个线程修改该变量的值,那么采用原子操作可以不用添加互 ...

  7. golang的atomic原子操作

    1.atomic是最轻量级的锁,在一些场景中非常有效. 2.atomic的原理是CAS操作,该操作的优势是,可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作. 这可以大大的减少同步对程序 ...

  8. C++11之atomic原子操作

    atomic介绍 atomic对int.char.bool等数据结构进行了原子性封装,在多线程环境中,对std::atomic对象的访问不会造成竞争-冒险.利用std::atomic可实现数据结构的无 ...

  9. CUDA atomic原子操作

    CUDA的原子操作可以理解为对一个变量进行"读取-修改-写入"这三个操作的一个最小单位的执行过程,这个执行过程不能够再分解为更小的部分,在它执行过程中,不允许其他并行线程对该变量进 ...

最新文章

  1. pandas替换列值+1
  2. win10无线投屏_日臻薄技 | 手机如何投屏到电脑
  3. jQuery-1.9.1源码分析系列(十六)ajax——ajax处理流程以及核心函数
  4. 如何查看linux系统的体系结构
  5. 【POJ - 1942 】Paths on a Grid (组合数学,求组合数的无数种方法)
  6. 伪共享和缓存行填充,Java并发编程还能这么优化!
  7. centos系统linux复制命令行,linux系统CentOS7中find命令使用
  8. php 走马灯轮播,Vue.js轮播图走马灯代码实例(全)
  9. 自制 Word、Excel 批转 PDF 工具
  10. W25Q64简介(译)
  11. 3t中文 studio_mongodb,Studio 3T | MongoDB中文社区
  12. 专属资料包——Ps基础、788款设计师字体、Ps品牌样机、《20款顶级vi品牌手册》、100款以上世界级ui设计、《世界级插画集》
  13. vue:移动端使用ckplayer
  14. 在线IP到地理位置解析的API接口,IP到地理位置、所属组织名、AS号、域名反查
  15. win10 wmware 花屏_用了N年浑不知!原来Win10竟有这么多隐藏功能
  16. python裂缝检测_基于卷积神经网络的高楼外墙裂缝检测系统
  17. QML_ToolSeparator、ToolTip和Tumbler
  18. 8月30日科技联播:子弹短信总激活用户超过400万,亚马逊将成第2个万亿美元市值企业...
  19. 金融数据分析(八)计算股票的超额收益率
  20. 图片隐写查看神器stegsolve使用方法

热门文章

  1. c语言题歌唱比赛要答案的,难忘的唱歌比赛作文.doc
  2. python----计算邮资问题
  3. 产品经理在运营人员眼中是怎样的人?
  4. loopback地址用途
  5. Windows 10 虚拟机很卡
  6. 插装式两位两通电磁阀DSV-080-2NCP、DDSV-080-2NCP
  7. 解决 Ajax:Ensure CORS response header values are valid 跨域问题
  8. (转)《完全用Linux工作》
  9. 低代码如何自定义主题?一文教会你
  10. 7天学会NodeJs(里面讲了很多,最后一个大例子,更是讲了一些工程开发流程,很有收获)