c++ atomic atomic_compare_exchange_weak CAS编程

别来无恙 提交于 2020-03-26 16:54:07

3 月,跳不动了?>>>

概述

      在多线程应用中有个很典型的业务就是共同争抢一个某一个资源,比如很典型的秒杀,很多的线程在争夺某一个资源。本文以一个最简单的例子说明,2万个线程一起争夺一个变量+1的操作。

只使用atomic

    在https://my.oschina.net/u/3707404/blog/3211668中,使用atomic类型可以避免多线程加锁导致的效率问题,同时保持资源安装,但是atomic本身有也其缺陷,其不能保证访问的一致性,即当100个线程都执行+1他能保证每一个线程的操作都会成功,但是如果你访问这个变量,其不保证每一个线程都看到的是最新的值,通过下面简单的代码你就会体会到。

 

#include <vector>
#include <queue>
#include <iostream>
#include <boost/thread.hpp>
#include <chrono>

class Task {
public:
    std::atomic<int> queue;
    boost::mutex mutex;

    Task() {
        queue = 0;
    }

    void get() {
        if (queue == 0) {
            usleep(1);
            queue++;
        }
    }
};

int main(int argc, char **argv) {
    auto begin = std::chrono::high_resolution_clock::now();
    boost::thread_group threads;
    boost::shared_ptr<Task> task = boost::make_shared<Task>();
    for (int i = 0; i < 5000; i++) {
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
    }
    threads.join_all();
    std::cout << "current value " << task->queue << " \n";
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() << "ns" << std::endl;
    return 0;
}

输出结果

current value 6
537372675ns

加锁

    接下来我们尝试加锁

#include <vector>
#include <queue>
#include <iostream>
#include <boost/thread.hpp>
#include <chrono>

class Task {
public:
    std::atomic<int> queue;
    boost::mutex mutex;

    Task() {
        queue = 0;
    }

    void get() {
        boost::lock_guard<boost::mutex> lock{mutex};
        if (queue == 0) {
            usleep(1);
            queue++;
        }
    }

};

int main(int argc, char **argv) {
    auto begin = std::chrono::high_resolution_clock::now();
    boost::thread_group threads;
    boost::shared_ptr<Task> task = boost::make_shared<Task>();
    for (int i = 0; i < 5000; i++) {
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
    }
    threads.join_all();
    std::cout << "current value " << task->queue << " \n";
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() << "ns" << std::endl;
    return 0;
}

current value 1
699948447ns

加锁可以解决资源问题,但是不可避免的导致性能的浪费。

 

CAS操作

#include <vector>
#include <queue>
#include <iostream>
#include <boost/thread.hpp>
#include <chrono>

class Task {
public:
    std::atomic<int> queue;
    boost::mutex mutex;

    Task() {
        queue = 0;
    }

    void get() {
        int n = 0;
        usleep(1);
        if (std::atomic_compare_exchange_weak(&queue, &n, n + 1)) {
            std::cout << "i got the atomic add \n";
        }
    }
};

int main(int argc, char **argv) {
    auto begin = std::chrono::high_resolution_clock::now();
    boost::thread_group threads;
    boost::shared_ptr<Task> task = boost::make_shared<Task>();
    for (int i = 0; i < 5000; i++) {
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
        threads.create_thread(boost::bind(&Task::get, task));
    }
    threads.join_all();
    std::cout << "current value " << task->queue << " \n";
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count() << "ns" << std::endl;
    return 0;
}

i got the atomic add
current value 1
484698816ns

可以看到使用atomic_compare_exchange_weak机制是性能最好,并同时保证了资源的安全。

 

总结

    atomic_compare_exchange_weak是一种兼顾性能和资源安全的多线程同步模式,https://en.cppreference.com/w/c/atomic/atomic_compare_exchange

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!