无锁编程实例

无锁编程实例最近在研究nginx的自旋锁的时候,又见到了GCCCAS原子操作,于是决定动手分析下CAS实现的无锁到底性能如何,网上关于CAS实现无锁的文章很多,但少有研究这种无锁的性能提升的文章,这里就以实验结果和我自己的理解逐步展开。1.什么是CAS原子操作在研究无锁之前,我们需要首先了解一下CAS原子操作——Compare&Set,或是Compare&Swap,现在

大家好,又见面了,我是你们的朋友全栈君。

  • 最近在研究nginx的自旋锁的时候,又见到了GCC CAS原子操作,于是决定动手分析下CAS实现的无锁到底性能如何,网上关于CAS实现无锁的文章很多,但少有研究这种无锁的性能提升的文章,这里就以实验结果和我自己的理解逐步展开。

  • 1.什么是CAS原子操作

    在研究无锁之前,我们需要首先了解一下CAS原子操作——Compare & Set,或是 Compare & Swap,现在几乎所image有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。

    大家应该还记得操作系统里面关于“原子操作”的概念,一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。有了这个原子操作这个保证我们就可以实现无锁了。

    CAS原子操作在维基百科中的代码描述如下:

       1: int compare_and_swap(int* reg, int oldval, int newval)
       2: {
         
         
       3:   ATOMIC();
       4:   int old_reg_val = *reg;
       5:   if (old_reg_val == oldval)
       6:      *reg = newval;
       7:   END_ATOMIC();
       8:   return old_reg_val;
       9: }

  • 也就是检查内存*reg里的值是不是oldval,如果是的话,则对其赋值newval。上面的代码总是返回old_reg_value,调用者如果需要知道是否更新成功还需要做进一步判断,为了方便,它可以变种为直接返回是否更新成功,如下:

  •    1: bool compare_and_swap (int *accum, int *dest, int newval)
       2: {
         
         
       3:   if ( *accum == *dest ) {
          
          
       4:       *dest = newval;
       5:       return true;
       6:   }
       7:   return false;
       8: }

    除了CAS还有以下原子操作:

    • Fetch And Add,一般用来对变量做 +1 的原子操作。
         1: << atomic >>
         2: function FetchAndAdd(address location, int inc) {
              
              
         3:     int value := *location
         4:     *location := value + inc
         5:     return value
         6: }

       
      Test-and-set,写值到某个内存位置并传回其旧值。汇编指令BST。
         1: #define LOCKED 1
         2:  
         3: int TestAndSet(int* lockPtr) {
              
              
         4:     int oldValue;
         5:  
         6:     // Start of atomic segment
         7:     // The following statements should be interpreted as pseudocode for
         8:     // illustrative purposes only.
         9:     // Traditional compilation of this code will not guarantee atomicity, the
        10:     // use of shared memory (i.e. not-cached values), protection from compiler
        11:     // optimization, or other required properties.
        12:     oldValue = *lockPtr;
        13:     *lockPtr = LOCKED;
        14:     // End of atomic segment
        15:  
        16:     return oldValue;
        17: }

       
    • Test and Test-and-set,用来实现多核环境下互斥锁,
         1: boolean locked := false // shared lock variable
         2: procedure EnterCritical() {
              
              
         3:   do {
              
              
         4:     while (locked == true) skip // spin until lock seems free
         5:   } while TestAndSet(locked) // actual atomic locking
         6: }

    2.CAS 在各个平台下的实现

     

    2.1 Linux GCC 支持的 CAS

    GCC4.1+版本中支持CAS的原子操作(完整的原子操作可参看 GCC Atomic Builtins

       1: bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
       2: type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

    2.2  Windows支持的CAS

    在Windows下,你可以使用下面的Windows API来完成CAS:(完整的Windows原子操作可参看MSDN的InterLocked Functions

  •    1: InterlockedCompareExchange ( __inout LONG volatile *Target,
       2:                                 __in LONG Exchange,
       3:                                 __in LONG Comperand);

    2.3  C++ 11支持的CAS

    C++11中的STL中的atomic类的函数可以让你跨平台。(完整的C++11的原子操作可参看 Atomic Operation Library

       1: template< class T >
       2: bool atomic_compare_exchange_weak( std::atomic<T>* obj,
       3:                                    T* expected, T desired );
       4: template< class T >
       5: bool atomic_compare_exchange_weak( volatile std::atomic<T>* obj,
       6:                                    T* expected, T desired );

     

  • 3.CAS原子操作实现无锁的性能分析

     

    • image3.1测试方法描述

     
             这里由于只是比较性能,所以采用很简单的方式,创建10个线程并发执行,每个线程中循环对全局变量count进行++操作(i++),循环加2000000次,这必然会涉及到并发互斥操作,在同一台机器上分析 加普通互斥锁、CAS实现的无锁、Fetch And Add实现的无锁消耗的时间,然后进行分析。

     

    3.2 加普通互斥锁代码

       1: #include <stdio.h>
       2: #include <stdlib.h>
       3: #include <pthread.h>
       4: #include <time.h>
       5: #include "timer.h"
       6:  
       7: pthread_mutex_t mutex_lock;
       8: static volatile int count = 0;
       9: void *test_func(void *arg)
      10: {
         
         
      11:         int i = 0;
      12:         for(i = 0; i < 2000000; i++)
      13:         {
         
         
      14:                 pthread_mutex_lock(&mutex_lock);
      15:                 count++;
      16:                 pthread_mutex_unlock(&mutex_lock);
      17:         }
      18:         return NULL;
      19: }
      20:  
      21: int main(int argc, const char *argv[])
      22: {
         
         
      23:     Timer timer; // 为了计时,临时封装的一个类Timer。
      24:     timer.Start();    // 计时开始
      25:     pthread_mutex_init(&mutex_lock, NULL);
      26:     pthread_t thread_ids[10];
      27:     int i = 0;
      28:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)
      29:     {
         
         
      30:         pthread_create(&thread_ids[i], NULL, test_func, NULL);
      31:     }
      32:  
      33:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)
      34:     {
         
         
      35:         pthread_join(thread_ids[i], NULL);
      36:     }
      37:  
      38:     timer.Stop();// 计时结束
      39:     timer.Cost_time();// 打印花费时间
      40:     printf("结果:count = %d\n",count);
      41:  
      42:     return 0;
      43: }

    注:Timer类仅作统计时间用,其实现在文章最后给出。

     

    3.2 CAS实现的无锁

     

       1: #include <stdio.h>
       2: #include <stdlib.h>
       3: #include <pthread.h>
       4: #include <unistd.h>
       5: #include <time.h>
       6: #include "timer.h"
       7:  
       8: int mutex = 0;
       9: int lock = 0;
      10: int unlock = 1;
      11:  
      12: static volatile int count = 0;
      13: void *test_func(void *arg)
      14: {
         
         
      15:         int i = 0;
      16:         for(i = 0; i < 2000000; i++)
      17:     {
         
         
      18:         while (!(__sync_bool_compare_and_swap (&mutex,lock, 1) ))usleep(100000);
      19:          count++;
      20:          __sync_bool_compare_and_swap (&mutex, unlock, 0);
      21:         }
      22:         return NULL;
      23: }
      24:  
      25: int main(int argc, const char *argv[])
      26: {
         
         
      27:     Timer timer;
      28:     timer.Start();
      29:     pthread_t thread_ids[10];
      30:     int i = 0;
      31:  
      32:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)
      33:     {
         
         
      34:             pthread_create(&thread_ids[i], NULL, test_func, NULL);
      35:     }
      36:  
      37:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++)
      38:     {
         
         
      39:             pthread_join(thread_ids[i], NULL);
      40:     }
      41:  
      42:     timer.Stop();
      43:     timer.Cost_time();
      44:     printf("结果:count = %d\n",count);
      45:  
      46:     return 0;
      47: }
      48:  

     

    3.4 Fetch And Add 原子操作

     

       1: #include <stdio.h>
       2: #include <stdlib.h>
       3: #include <pthread.h>
       4: #include <unistd.h>
       5: #include <time.h>
       6: #include "timer.h"
       7:  
       8: static volatile int count = 0;
       9: void *test_func(void *arg)
      10: {
       
       
      11:         int i = 0;
      12:         for(i = 0; i < 2000000; i++)
      13:         {
       
       
      14:             __sync_fetch_and_add(&count, 1);
      15:         }
      16:         return NULL;
      17: }
      18:  
      19: int main(int argc, const char *argv[])
      20: {
       
       
      21:     Timer timer;
      22:     timer.Start();
      23:     pthread_t thread_ids[10];
      24:     int i = 0;
      25:  
      26:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++){
       
       
      27:             pthread_create(&thread_ids[i], NULL, test_func, NULL);
      28:     }
      29:  
      30:     for(i = 0; i < sizeof(thread_ids)/sizeof(pthread_t); i++){
       
       
      31:             pthread_join(thread_ids[i], NULL);
      32:     }
      33:  
      34:     timer.Stop();
      35:     timer.Cost_time();
      36:     printf("结果:count = %d\n",count);
      37:     return 0;
      38: }
      39:  

     

    4 实验结果和分析

    在同一台机器上,各运行以上3份代码10次,并统计平均值,其结果如下:(单位微秒)

    image

    由此可见,无锁操作在性能上远远优于加锁操作,消耗时间仅为加锁操作的1/3左右,无锁编程方式确实能够比传统加锁方式效率高,经上面测试可以发现,可以快到3倍左右。所以在极力推荐在高并发程序中采用无锁编程的方式可以进一步提高程序效率。

    5.时间统计类Timer

    timer.h

       1: #ifndef TIMER_H
       2: #define TIMER_H
       3:  
       4: #include <sys/time.h>
       5: class Timer
       6: {
          
          
       7: public:    
       8:     Timer();
       9:     // 开始计时时间
      10:     void Start();
      11:     // 终止计时时间
      12:     void Stop();
      13:     // 重新设定
      14:     void Reset();
      15:     // 耗时时间
      16:     void Cost_time();
      17: private:
      18:     struct timeval t1;
      19:     struct timeval t2;
      20:     bool b1,b2;
      21: };
      22: #endif

    timer.cpp

       1: #include "timer.h"
       2: #include <stdio.h>
       3:  
       4: Timer::Timer()
       5: {
          
          
       6:     b1 = false;
       7:     b2 = false;
       8: }
       9: void Timer::Start()
      10: {
          
          
      11:     gettimeofday(&t1,NULL);  
      12:     b1 = true;
      13:     b2 = false;
      14: }
      15:  
      16: void Timer::Stop()
      17: {    
      18:     if (b1 == true)
      19:     {
          
          
      20:         gettimeofday(&t2,NULL);  
      21:         b2 = true;
      22:     }
      23: }
      24:  
      25: void Timer::Reset()
      26: {    
      27:     b1 = false;
      28:     b2 = false;
      29: }
      30:  
      31: void Timer::Cost_time()
      32: {
          
          
      33:     if (b1 == false)
      34:     {
          
          
      35:         printf("计时出错,应该先执行Start(),然后执行Stop(),再来执行Cost_time()");
      36:         return ;
      37:     }
      38:     else if (b2 == false)
      39:     {
          
          
      40:         printf("计时出错,应该执行完Stop(),再来执行Cost_time()");
      41:         return ;
      42:     }
      43:     else
      44:     {
          
          
      45:         int usec,sec;
      46:         bool borrow = false;
      47:         if (t2.tv_usec > t1.tv_usec)
      48:         {
          
          
      49:             usec = t2.tv_usec - t1.tv_usec;
      50:         }
      51:         else
      52:         {
          
          
      53:             borrow = true;
      54:             usec = t2.tv_usec+1000000 - t1.tv_usec;
      55:         }
      56:  
      57:         if (borrow)
      58:         {
          
          
      59:             sec = t2.tv_sec-1 - t1.tv_sec;
      60:         }
      61:         else
      62:         {
          
          
      63:             sec = t2.tv_sec - t1.tv_sec;
      64:         }
      65:         printf("花费时间:%d秒 %d微秒\n",sec,usec);
      66:     }
      67: }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/136262.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 入Silverlight QQ群者必读「建议收藏」

    入Silverlight QQ群者必读「建议收藏」建立本技术群的目的,是为拥有共同爱好的你们提供一个交流平台,我们在此相约同游,共同分享彼此的Silverlight与WPF开发技术心得经验,当然也可以讲饮讲食,一起FB……希望各群友可以相互尊重,文明上网,将本群建成一个和谐的大家庭。         正所谓国有国法、群有群规!希望大家配合遵守!           1.严禁攻击人身的言论;在群中公然侮辱他人、捏造事实诽谤他人、对他人进

  • JAVA Calendar方法详解「建议收藏」

    JAVA Calendar方法详解「建议收藏」 究竟什么是一个Calendar呢?中文的翻译就是日历,那我们立刻可以想到我们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢?比如有:月份的定义-阳`(公)历一年12个月,每个月的天数各不同;阴(农)历,每个月固定28天每周的第一天-阳(公)历星期日是第一天;阴(农)历,星期一是第一天实际上,在历史上有着许多种纪元的方法。它们的差异实在太大了,比如说一个人的生日是”八月八日”

  • 4G网络架构_三大网络架构

    4G网络架构_三大网络架构1,4G是第四代移动通信技术,该技术包括TD-LTE和FDD-LTE两种制式,严格意义上来讲LTE只是3.9G,只有升级版的LTEAdvanced才满足国际电信联盟对4G的要求。4G是集3G与WLAN于一体,并能够快速传输数据、高质量、音频、视频和图像等。4G能够以100Mbps以上的速度下载,比目前的家用宽带ADSL(4兆)快25倍,并能够满足几乎所有用户对于无线服务的要求。

  • 3_1符合python语言变量_以下选项中符合Python语言变量命名规则的是「建议收藏」

    3_1符合python语言变量_以下选项中符合Python语言变量命名规则的是「建议收藏」【单选题】以下选项中,不是Python语言特点的是【单选题】较小的尺寸应离轮廓线较近,较大的尺寸线离轮廓线较远。()【单选题】关于Python语言的变量,以下选项中说法正确的是【单选题】1825年英国的克路斯发明了真正具有仪表特征是:()。【判断题】按水表计数器形式分,水表可分为液封水表、干式水表、湿式水表。【单选题】尺寸线和尺寸界线()绘制。【单选题】以下不是python中的关…

  • 使用phpMyAdmin批量修改Mysql数据表前缀的方法

    使用phpMyAdmin批量修改Mysql数据表前缀的方法

    2021年10月14日
  • javascript数组去重的几种常见方法_前端数组去重的方法

    javascript数组去重的几种常见方法_前端数组去重的方法JavaScript 高性能数组去重

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号