zoukankan      html  css  js  c++  java
  • [转]c++11 原子操作

    [转自https://www.cnblogs.com/lidabo/p/7890526.html]

    所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。

    在以往的C++标准中并没有对原子操作进行规定,我们往往是使用汇编语言,或者是借助第三方的线程库,例如intel的pthread来实现。在新标准C++11,引入了原子操作的概念,并通过这个新的头文件提供了多种原子操作数据类型,例如,atomic_bool,atomic_int等等,如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率。

    我们还是来看一个实际的例子。假若我们要设计一个广告点击统计程序,在服务器程序中,使用多个线程模拟多个用户对广告的点击:

     1 #include <boost/thread/thread.hpp>
     2 #include <atomic> 
     3 #include <iostream>
     4 #include <time.h>
     5 
     6 using namespace std;
     7 // 全局的结果数据 
     8 long total = 0 9 // 点击函数
    10 void click()
    11 {
    12     for(int i=0; i<1000000;++i)
    13     {
    14         // 对全局数据进行无锁访问 
    15         total += 1;     
    16     }
    17 }
    18  
    19 int main(int argc, char* argv[])
    20 {
    21     // 计时开始
    22     clock_t start = clock();
    23     // 创建100个线程模拟点击统计
    24     boost::thread_group threads;
    25     for(int i=0; i<100;++i) 
    26     {
    27         threads.create_thread(click);
    28     }
    29     threads.join_all();
    30     // 计时结束
    31     clock_t finish = clock();
    32     // 输出结果
    33     cout<<"result:"<<total<<endl;
    34     cout<<"duration:"<<finish -start<<"ms"<<endl;
    35     return 0;
    36 }

    从执行的结果来看,这样的方法虽然非常快,但是结果不正确

    E:SourceCodeMinGW>thread.exe
    result:87228026
    duration:528ms

    很自然地,我们会想到使用互斥对象来对全局共享资源的访问进行保护,于是有了下面的实现:

     1 long total = 0;
     2 // 对共享资源进行保护的互斥对象
     3 mutex m;
     4 
     5 void click()
     6 {
     7     for(int i=0; i<1000000;++i)
     8     {
     9         // 访问之前,锁定互斥对象
    10         m.lock();
    11         total += 1;
    12         // 访问完成后,释放互斥对象 
    13         m.unlock();
    14     }
    15 }

     互斥对象的使用,保证了同一时刻只有唯一的一个线程对这个共享进行访问,从执行的结果来看,互斥对象保证了结果的正确性,但是也有非常大的性能损失,从刚才的528ms变成了现在的8431,用了原来时间的10多倍的时间。这个损失够大。

    E:SourceCodeMinGW>thread.exe
    result:100000000
    duration:8431ms

    如果是在C++11之前,我们的解决方案也就到此为止了,但是,C++对性能的追求是永无止境的,他总是想尽一切办法榨干CPU的性能。在C++11中,实现了原子操作的数据类型(atomic_bool,atomic_int,atomic_long等等),对于这些原子数据类型的共享资源的访问,无需借助mutex等锁机制,也能够实现对共享资源的正确访问。

     1 // 引入原子数据类型的头文件
     2 #include <atomic> 
     3  
     4 
     5 // 用原子数据类型作为共享资源的数据类型
     6 atomic_long total(0);
     7 //long total = 0;
     8  
     9 void click()
    10 {
    11     for(int i=0; i<1000000;++i)
    12     {
    13         // 仅仅是数据类型的不同而以,对其的访问形式与普通数据类型的资源并无区别
    14         total += 1;
    15     }
    16

    我们来看看使用原子数据类型之后的效果如何:

    E:SourceCodeMinGW>thread.exe
    result:100000000
    duration:2105ms

    结果正确!耗时只是使用mutex互斥对象的四分之一!也仅仅是不采用任何保护机制的时间的4倍。可以说这是一个非常不错的成绩了。

    原子操作的实现跟普通数据类型类似,但是它能够在保证结果正确的前提下,提供比mutex等锁机制更好的性能,如果我们要访问的共享资源可以用原子数据类型表示,那么在多线程程序中使用这种新的等价数据类型,是一个不错的选择。

    【转自:http://imcc.blogbus.com/logs/179131763.html】

  • 相关阅读:
    【LeetCode】Validate Binary Search Tree
    【LeetCode】Search in Rotated Sorted Array II(转)
    【LeetCode】Search in Rotated Sorted Array
    【LeetCode】Set Matrix Zeroes
    【LeetCode】Sqrt(x) (转载)
    【LeetCode】Integer to Roman
    贪心算法
    【LeetCode】Best Time to Buy and Sell Stock III
    【LeetCode】Best Time to Buy and Sell Stock II
    CentOS 6 上安装 pip、setuptools
  • 原文地址:https://www.cnblogs.com/yi-mu-xi/p/9922596.html
Copyright © 2011-2022 走看看