C++11新特性之十五:std::atomic

在以往的C++标准中并没有对原子操作进行规定,在新标准C++11,引入了原子操作的概念,并通过头文件#include <atomic>提供了多种原子操作数据类型,这些类型上的所有操作都是原子的。如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率。
此头文件主要声明了两大类原子对象:std::atomic_flag和std::atomic,另外还声明了一套C风格的原子类型和与C兼容的原子操作的函数

一.std::atomic_flag

std::automic_flag是bool型原子对象,执行test_and_set和clear两种操作。
结合test_and_set()和clear(),std::atomic_flag对象可以当作一个简单的自旋锁(spin lock)使用。如果在初始化时没有明确指定宏ATOMIC_FLAG_INIT,那么新创建的std::atomic_flag对象的状态是未指定的(unspecified),既没有被set也没有被clear;如果使用该宏初始化,该std::atomic_flag对象在创建时处于clear状态。

1.test_and_set

返回该std::atomic_flag对象当前状态。检查flag是否被设置,若被设置直接返回true,若没有设置则设置flag为true后再返回false。该函数是原子的。

2.clear

清除std::atomic_flag对象的标志位,即设置std::atomic_flag的值为false

// using atomic_flag as a lock
#include <iostream>      
#include <atomic>        
#include <thread>         
#include <vector>          
#include <sstream>      

std::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();
    return 0;
}

二.std::atomic

std::atomic是类模板,提供了比std::atomic_flag更加完善的功能。std::atomic对int, char, bool等数据结构进行原子性封装,在多线程环境中,对std::atomic对象的访问不会造成竞争-冒险。利用std::atomic可实现数据结构的无锁设计。
与std::atomic_flag类似,std::atomic也提供了一个宏ATOMIC_VAR_INIT(val)用于初始化std::atomic对象,例如std::atomic<int> a = ATOMIC_VAR_INIT(1)。提供这个宏的目的是为了兼容C实现(This macro exists for compatibility with C implementations, in which it is used as a constructor-like function for(default-constructed) atomic objects),在C++中,可以直接通过构造函数初始化,例如std::atomic<int> a(1)

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

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

void count1m(int i)
{
    while (!ready) { std::this_thread::yield(); }      // wait for the ready signal
    for (volatile int i = 0; i < 1000000; ++i) {}      // go!, count to 1 million
    if (!winner.test_and_set())
    {
        std::cout << "winner: " << i << std::endl;
    }
}

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

    ready = true;

    for (int i=0; i<10; i++)
    {
        ths[i].join();
    }

    return 0;
}

在本例中,执行read=true之后,所有线程结束空等。winner被初始化为ATOMIC_FLAG_INIT,最先执行winner.test_and_set并返回false的线程为winner。

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

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

void set_foo(int x)
{
    foo = x;
}

void print_foo()
{
    while (foo == 0)
    {
        std::this_thread::yield();
    }
    std::cout << "foo: " << foo << std::endl;
}
int main()
{
    std::thread print_th(print_foo);
    std::thread set_th(set_foo, 10);
    print_th.join();
    set_th.join();
    return 0;
}

set_foo用于设置atomic<int>对象的值,print_foo用于打印atomic<int>对象的值,毫无疑问,输出打印foo: 10。std::atomic对象的值的读取和写入还可使用load和store实现。

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

std::atomic<int> foo(0);
std::atomic_flag lock = ATOMIC_FLAG_INIT;

void add_foo()
{
    while (1)
    {
        foo++;
        while (lock.test_and_set());
        std::cout << "add: " << foo << std::endl;
        lock.clear();
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

void sub_foo()
{
    while (1)
    {
        foo--;
        while (lock.test_and_set());
        std::cout << "sub: " << foo << std::endl;
        lock.clear();
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}
int main()
{
    std::thread th2 = std::thread(add_foo);
    std::thread th1 = std::thread(sub_foo);
    th1.join();
    th2.join();
    return 0;
}

atomic<int>支持++和--的原子操作
除了直接使用std::atomic<>类型模板外,还可以使用下表中的原子类型集,如下表所示

原子类型相关特化类
atomic_boolstd::atomic<bool>
atomic_charstd::atomic<char>
atomic_scharstd::atomic<signed char>
atomic_ucharstd::atomic<unsigned char>
atomic_intstd::atomic<int>
atomic_uintstd::atomic<unsigned>
atomic_shortstd::atomic<short>
atomic_ushortstd::atomic<unsigned short>
atomic_longstd::atomic<long>
atomic_ulongstd::atomic<unsigned long>
atomic_llongstd::atomic<long long>
atomic_ullongstd::atomic<unsigned long long>
atomic_char16_tstd::atomic<char16_t>
atomic_char32_tstd::atomic<char32_t>
atomic_wchar_tstd::atomic<wchar_t>

三.atomic与mutex性能对比

在本文开头提到原子操作避免了锁的使用,提高了效率。下面将对此进行验证

1.使用mutex

#include <iostream>
#include <ctime>
#include <mutex>
#include <vector>
#include <thread>

std::mutex mtx;
int count = 0;

void threadFun()
{
    for (int i = 0; i < 10000; i++)
    {
        mtx.lock();
        count++;
        mtx.unlock();
    }
}

int main(void)
{
    clock_t start_time = clock();

    std::vector<std::thread> ths;
    for (int i = 0; i < 10; i++)
    {
        ths.push_back(std::thread(threadFun));
    }

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

    std::cout << "Count number:" << count << std::endl;
    clock_t end_time = clock();
    std::cout << "Elapse: " << end_time - start_time << "ms" << std::endl;

    return 0;
}


2.使用atomic

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

 

std::atomic_int count(0);

void threadFun()
{
    for (int i = 0; i < 10000; i++)
    {
        count++;
    }
}

int main(void)
{
    clock_t start_time = clock();

    std::vector<std::thread> ths;
    for (int i = 0; i < 10; i++)
    {
        ths.push_back(std::thread(threadFun));
    }

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

    std::cout << "Count number:" << count << std::endl;
    clock_t end_time = clock();
    std::cout << "Elapse: " << end_time - start_time << "ms" << std::endl;

    return 0;
}


总结:从上面的截图可以发现,第一张图用时5ms,第二张图用时2ms,使用原子操作能大大的提高程序的运行效率。 

原文链接:https://blog.csdn.net/caoshangpa/article/details/51712643

  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

草上爬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值