atomic_flag

在这里插入图片描述
可以看出,atomic_flag 只有默认构造函数,在C++20之前,声明时需要显示初始化,C++20之后,就不需要显示初始化了,自动初始化。
在这里插入图片描述

test_and_set()

设置这个对象的标志位

编译
g++ text.cpp -o text -std=c++11

#include<iostream>
#include<atomic>
#include<thread>


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

std::atomic_flag winner=ATOMIC_FLAG_INIT;


void func(int id) {

    while (!ready) {
        std::this_thread::yield();  //表示该线程可以让出时间片
    }

    for (int i = 0;i<1000000;i++) {
    }

    if (!winner.test_and_set()) {
        std::cout<<"thread #"<<id<<"won"<<std::endl;
    }
}


int main() {

    std::vector<std::thread> threads;
    for (int i = 0;i<10;i++) {
        threads.push_back(std::thread(func,i));
    }
    ready = true;

    for (auto & th:threads) {
        th.join();
    }

    return 0;

}
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text 
thread #7won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text
thread #9won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text
thread #2won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text
thread #1won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text
thread #9won

通过结果可以看出,只有一个线程运行成功了,可以知道winner.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;
返回winner之前有没有设置过,如果设置过了返回true,没设置过返回false;

std::atomic_flag::clear()

清除这个对象的标志位,设置标志位为false

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

这个demo可以看出来,通过test_and_set()可以严格控制顺序

编译
g++ text.cpp -o text -std=c++11

#include<thread>
#include<iostream>
#include<atomic>
#include<vector>
std::atomic_flag lock_stream = ATOMIC_FLAG_INIT;
std::stringstream ss;

void func(int id) {
    //只有第一次设置的lock_stream 才返回false;
    while (lock_stream.test_and_set()) {
    }

    ss<<"thread: "<<id<<std::endl;

    lock_stream.clear();  //清除这个标记位
}


int main() {
    std::vector<std::thread> threads;
    for (int i = 0;i<10;i++) {
        threads.push_back(std::thread(func,i));
    }

    for (auto &th:threads) {
        th.join();
    }

    std::cout<<ss.str()<<std::endl;
    return 0;
}

xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text2
thread: 0
thread: 1
thread: 2
thread: 3
thread: 4
thread: 5
thread: 6
thread: 7
thread: 8
thread: 9


根据这两个函数设计一个简单的自旋锁

std::atomic_flag lock = ATOMIC_FLAG_INIT;
void f(int n) {

    while (lock.test_and_set(std::memory_order_acquire)) {}  //第一次设置会返回false,第二次访问会返回true

    //临界区
    std::cout<<"Output from thread: "<<n<<std::endl;
    lock.clear(std::memory_order_release);
}


int main() {
    std::vector<std::thread> threads;
    for (int i = 0;i<10;i++) {
        threads.emplace_back(f, i);
    }

    for (auto &th:threads) {
        th.join();
    }

    // std::cout<<ss.str()<<std::endl;
    return 0;
}

xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text2                          
Output from thread: 0
Output from thread: 1
Output from thread: 3
Output from thread: 4
Output from thread: 6
Output from thread: 2
Output from thread: 5
Output from thread: 7
Output from thread: 8
Output from thread: 9

atomic

std::atomic 是模板类.

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);
};

针对整形(integral)的特化版本:其中 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::stomic 构造函数

在这里插入图片描述

  1. 默认构造函数,由默认构造函数创建的 std::atomic 对象处于未初始化(uninitialized)状态,对处于未初始化(uninitialized)状态 std::atomic象可以由 atomic_init 函数进行初始化。
  2. 初始化构造函数,由类型 T初始化一个 std::atomic对象,初始化操作不是原子的
  3. 拷贝构造函数被禁用。

看一个例子,谁先数完数,谁赢

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::atomic<bool> ready(false);
std::atomic_flag winner = ATOMIC_FLAG_INIT;

void func(int id) {
    while (!ready) {
        std::this_thread::yield();
    }
    for (int i = 0;i<1000000;i++){}

    if (!winner.test_and_set()) {
        std::cout<<"thread #"<<id<<"won"<<std::endl;
    }
}


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.emplace_back(func, i);
    ready = true;

    for (auto& th:threads) {
        th.join();
    }

    return 0;
}

xutianyang.10@ZBMac-KYTH7H97M4 atomic % g++ -o text3 text3.cpp -std=c++11
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3 
spawning 10 threads that count to 1 million..
thread #7won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3
spawning 10 threads that count to 1 million..
thread #9won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3
spawning 10 threads that count to 1 million..
thread #10won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3
spawning 10 threads that count to 1 million..
thread #9won
xutianyang.10@ZBMac-KYTH7H97M4 atomic % 

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

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

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::atomic<int> foo(0);

void print_foo() {
    while (foo == 0) {
        std::this_thread::yield();
    }

    std::cout<<"foo:"<<foo<<std::endl;   
}

void set_foo(int i) {
    foo = i;    // 调用 std::atomic::operator=().
}

int main() {

    std::vector<std::thread> threads;

    std::thread first(print_foo);
    std::thread second(set_foo, 10);
    first.join();
    second.join();
    return 0;
}


xutianyang.10@ZBMac-KYTH7H97M4 atomic % g++ -o text3 text3.cpp -std=c++11
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3                          
foo:10


基本 std::atomic 类型操作

is_lock_free()

用来检查该原子变量是不是无锁的,因为有些类型可能不支持。

bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;

std::atomic<int> atomicInt;
std::atomic<bool> atomicBool;
std::atomic<long long> atomicLongLong;

std::cout << "atomic<int> is lock-free: " << atomicInt.is_lock_free() << std::endl;
std::cout << "atomic<bool> is lock-free: " << atomicBool.is_lock_free() << std::endl;
std::cout << "atomic<long long> is lock-free: " << atomicLongLong.is_lock_free() << std::endl;


xutianyang.10@ZBMac-KYTH7H97M4 atomic % g++ -o text3 text3.cpp -std=c++11
xutianyang.10@ZBMac-KYTH7H97M4 atomic % ./text3                          
foo:10
atomic<int> is lock-free: 1
atomic<bool> is lock-free: 1
atomic<long long> is lock-free: 1

store()和load()

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

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;

加载被封装的值

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

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::atomic<int> foo(0);

void print_foo() {
    int x;
    do {
        x = foo.load(std::memory_order_relaxed);  //加载foo值
    }while (x==0);

    std::cout<<"foo:"<<foo<<std::endl;
}

void set_foo(int i) {

    foo.store(i, std::memory_order_relaxed);   //设置foo值为i
}


int main() {

    std::vector<std::thread> threads;

    std::thread first(print_foo);
    std::thread second(set_foo, 10);
    first.join();
    second.join();
    return 0;
}

exchange()

读取并且修改被封装的值为val,并且返回旧值。整个过程是原子的。

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;

#include<iostream>
#include<atomic>
#include<thread>


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

std::atomic<bool> winner;


void func(int id) {

    while (!ready) {
        std::this_thread::yield();  //表示该线程可以让出时间片
    }

    for (int i = 0;i<1000000;i++) {
    }

    if (!winner.exchange(true)) {
        std::cout<<"thread #"<<id<<"won"<<std::endl;
    }
}


int main() {

    std::vector<std::thread> threads;
    for (int i = 0;i<10;i++) {
        threads.push_back(std::thread(func,i));
    }
    ready = true;

    for (auto & th:threads) {
        th.join();
    }

    return 0;

}

compare_exchange_weak()

expected期盼的值,val要修改的值。
比较被封装的值(weak)与expected值,是否相等。
如果旧值与expected相等,用val替换原子对象的值。
如果旧值与expected不相等,则将旧值赋值给expected。

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

在这里插入图片描述

头插方式取得的链表

#include <iostream>
#include <thread>
#include <vector>

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

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

void append(int val) {

    Node* new_node = new Node{val, head_list};


    //这个插入方式是线程安全的,是头插的方式
    while (!head_list.compare_exchange_weak(new_node->next, new_node)) {}

}



int main() {

    std::vector<std::thread> threads;

    for (int i = 0;i<10;i++) {
        threads.emplace_back(append, i);
    }
    for (auto  &th:threads) {
        th.join();
    }

    Node* head = head_list.load();
    while (head) {
        std::cout<<head->data<<std::endl;
        head = head->next;
    }
    return 0;
}

compare_exchange_strong()

这个跟compare_exchange_weak()的使用方法没有区别,但是weak可能会出现伪失败的情况,就是expected和旧值一样,也返回false。但是这种情况不影响原子性,失败了就失败了呗,再循环一次。所以weak适合循环条件。
strong则不会出现这种伪失败的条件。
在这里插入图片描述

更多推荐