C++11中原子变量std::atomic使用小记

C++11中引入了原子变量模板类型,原子变量的引入,极大的方便了开发者开发多并发程序。工作中也经常使用原子变量,今天针对具体类型std::atomic<unsigned long long>和自定义类型std::atomic<Node*>使用做个小小的总结。

std::atomic<unsigned long long>原子变量的常用操作有:

(1) -- ++操作

(2)store(写操作)

(3)load(读操作)

(4)exchange(交换数据)

(5)compare_exchange_weak (CAS)

(6)compare_exchange_strong (加强版的CAS,性能略差)

说明:

         compare_exchange_weak和compare_exchange_strong都是比较并更新操作,功能类似,compare_exchange_weak允许偶然出乎意料的返回false,在原始值和比较值一致时,通常比compare_exchange_strong有更高的性能。函数会传入比较值和更新值,进行如下操作:

1)如果原始值与比较值一致,则将原始值修改为更新值,并返回true

2)如果原始值与比较值不一致,则将比较值修改为原始值,并返回false

1.简单累加操作

在多线程中通常需要统计某个操作调用了多少次,此时可以利用原子变量统计比加锁更加高效和快捷,举例如下:

#include <atomic>
#include <thread>

static std::atomic<unsigned long long> s_sequence_index(0);

void no_lock_statistics() {
    std::thread th1([&]{
        for (int i = 0; i < 1000000; i++) {
            ++s_sequence_index;
        }
    });
    std::thread th2([&]{
        for (int i = 0; i < 1000000; i++) {
            ++s_sequence_index;
        }
    });
    std::thread th3([&]{
        for (int i = 0; i < 1000000; i++) {
            ++s_sequence_index;
        }
    });
    std::thread th4([&]{
        for (int i = 0; i < 1000000; i++) {
            ++s_sequence_index;
        }
    });
    th1.join();
    th2.join();
    th3.join();
    th4.join();
    std::cout << "statistics:: " << s_sequence_index.load() << std::endl;
}

int main() {
    no_lock_statistics();
    return 0;
}

运行结果如下:

通过运行看,4个线程同时对一个原子变量操作1000000次++,操作结束原子变量变为4000000,如果s_sequence_index改为普通的类型,操作结束结果不会变为4000000。

2.获取并更新值操作

在实际多线程开发中,往往需要比较,获取和更新某个变量,这一系列操作想要保证原子操作,如果没有原子变量,往往需要增加互斥锁。有了原子变量之后,操作就会变得很容易

#include <atomic>
#include <thread>

static std::atomic<unsigned long long> s_sequence_index(0);

uint64_t get_index() {
    unsigned long long _index = 0;
    do {
        _index = s_sequence_index;
    } while(!s_sequence_index.compare_exchange_weak(_index, _index + 1));
    return (uint64_t)_index;
}

void test_get_index() {
    for (int i = 0; i < 100; i++) {
        std::thread([&]{
            std::cout << get_index() << std::endl;
        }).detach();
    }
    getchar();
    std::cout << "end:: " << s_sequence_index << std::endl;
}

int main() {
    test_get_index();
    return 0;
}

运行结果如下:

如上图示,多线程不重复的获取原子变量的值,并对其进行更新,保证原子变量值的连续性和排他性。

 

自定义类型原子变量std::atomic<Node*>的常用操作有:

(1)store(写操作)

(2)load(读操作)

(3)exchange(交换数据)

(4)compare_exchange_weak (CAS)

(5)compare_exchange_strong

如下将展示使用原子操作多线程操作链表的例子

 

#include <atomic>
#include <thread>

template<typename T>
 class LockFreeList
 {
 private:
    struct Node
    {
        T _data;
        Node* _next;
    
        Node(T const& data): _data(data), _next(nullptr) {
        }
    };

public:
    LockFreeList() : _head(nullptr) {
    }

    void push(T const& data) {
        Node* new_node = new Node(data); 
        new_node->_next = _head.load();
        while (!_head.compare_exchange_weak(new_node->_next, new_node));
    }

    void show() {
        Node* tmpNode = _head;
        while(tmpNode) {
            std::cout << tmpNode->_data << " ";
            tmpNode = tmpNode->_next;
        }
        std::cout << std::endl;
    }

private:
    std::atomic<Node*> _head;
};

static LockFreeList<int> _s_lock_free_list;
static std::atomic<int> _s_free_int(0);

void test_lock_free_list() {
    for (int i = 0; i < 1000; i++) {
        std::thread([&](){
            _s_lock_free_list.push(_s_free_int++);
        }).detach();
    }
    getchar();
    _s_lock_free_list.show();
}

int main() {
    test_lock_free_list();
    return 0;
}

运行结果如下:

 通过测试以看到,数据被无重复的插入到了链表之中。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值