热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LinuxLockfree

原文链接http:blog.csdn.netpennyliangarticledetails5993138从该博客开始,会有一些小系


原文链接

http://blog.csdn.net/pennyliang/article/details/5993138



从该博客开始,会有一些小系列预计有4-5篇博文来介绍,锁的应用和实践,我们常常听到spin lock,wait-free,lock-free,这到底是怎么回事,我们能不能自己实现一个spin lock,原理是什么?这个小系列就讨论这个内容。

 

      首先我们来看两个基本操作compare_and_swap和fetch_and_add,基本上lock-free的操作都会依赖这两个基本的原子操作。特别是compare_and_swap这个原子操作,它源于IBM System 370,其包含三个参数:(1)共享内存的地址(*p),(2)该地址期望的值(old_value),(3)一个新值(new_value)。只有当*p == old_value时,才产生交换操作,返回真值,否则返回假值,相当于如下代码 :template

        bool CAS(T* addr, T exp, T val) //只有在整个函数过程具有原子性时才正确,实际的代码参照下面的汇编代码。
      {
              if(*addr == exp){
                                             *addr = val;
                                              return true;
                }
              return false;
      }

      在下面的代码中我们会看到compare_and_swap使用了lock指令,用于锁总线,setz会判断cmpxchg指令后ZF符号位是否置位,可以知道是否发生了一次交换。以下是一段可以执行的代码,void* sum(void*)函数通过不同的编译命令生成不同的代码,其结果都是用10个线程对一个全局变量进行加和的简单操作。但分别采用了pthread提供的mutex,fetch_and_add方法,完全无锁的方法,应用cas的三种方式,其中sum_with_cas_imp_yield就基本是spinlock的实现了。

      下一篇我来公布在我的测试机的实验结果,并且继续探讨其他lock-free的话题。

 

 

#include

#include

#include

#include

#include

#include

#if defined(__x86_64__) 

        #define ATOMICOPS_WORD_SUFFIX "q"  //64位环境下使用cmpxchgq命令

#else

        #define ATOMICOPS_WORD_SUFFIX "l"   //32位环境下使用cmpxchgl命令

#endif

static inline bool
compare_and_swap(volatile size_t *p, size_t val_old, size_t val_new){

        char ret;

        __asm__ __volatile__("lock; cmpxchg" ATOMICOPS_WORD_SUFFIX " %3, %0; setz %1"//lock命令锁总线,因此可以保证多核同步

                        : "=m"(*p), "=q"(ret)      //setz为ZF符号位是否置位,用于设置返回值

                        : "m"(*p), "r" (val_new), "a"(val_old)

                        : "memory");

        return (bool)ret;

}

static inline size_t
fetch_and_add(volatile size_t *p, size_t add){

        unsigned int ret;

        __asm__ __volatile__("lock; xaddl %0, %1"

                        :"=r" (ret), "=m" (*p)

                        : "0" (add), "m" (*p)

                        : "memory");

        return ret;

};

struct my_cas

{

        my_cas(unsigned char t):m_val_old(t){}

        size_t m_val_old;

        inline void try_continue(size_t val_old,size_t val_new){

                while(!compare_and_swap(&m_val_old,val_old,val_new)){};

        }

inline void add(size_t val_new){

                fetch_and_add(&m_val_old,val_new);

        }

};

volatile size_t g_uCount;

pthread_mutex_t g_tLck=PTHREAD_MUTEX_INITIALIZER;

my_cas mutex(1);

const size_t cnt_num = 10000000;

void* sum_with_mutex_lock(void*)

{

        for(int i=0;i
                pthread_mutex_lock(&g_tLck);

                g_uCount += 1;

                pthread_mutex_unlock(&g_tLck);

        }

};

void* sum_with_f_and_a(void*)  //用fetch_and_add原子操作来保证结果正确性。

{

        for(int i=0;i
                fetch_and_add(&g_uCount,1);

        }

};

void* sum_with_cas(void*)  //用CAS原子操作来模拟锁操作。

{      

        for(int i=0;i
        {      

                mutex.try_continue(1,0);

                g_uCount += 1;

                mutex.try_continue(0,1);  

        }

}

void* sum_with_cas_imp(void*)

{

        for(int i=0;i
                for(;;) {

                        size_t u = g_uCount;

                        if(compare_and_swap(&g_uCount,u,u+1)){   //在上一条语句和本条语句之间,g_uCount无篡改则进行加1,

                                break;        //break出该循环,否则重试,直到成功。

                        }

                }

        }

}

void* sum_with_cas_imp_yield(void*)

{

        for(int i=0;i
                for(;;) {

                        register size_t c = 1000; //

                        while(c){

                                size_t u = g_uCount;

                                if(compare_and_swap(&g_uCount,u,u+1)){

                                        break;

                                }

                                c--;

                        }

                        if(!c){

                               
syscall(SYS_sched_yield); //增加一次让渡CPU的机会,spin lock通常应有这种策略

                        }

                }

        }

}

void* sum_just_free(void*)

{      

        for(int i=0;i
                g_uCount += 1;

        }

}

void* sum(void*)

{

        #ifdef M_LOCK

         sum_with_mutex_lock(NULL);

        #endif

        #ifdef FETCH_AND_ADD

         sum_with_f_and_a(NULL);

        #endif

        #ifdef FREE

         sum_just_free(NULL);

        #endif

        #ifdef CAS

         sum_with_cas(NULL);

        #endif

        #ifdef CAS_IMP

         sum_with_cas_imp(NULL);

        #endif

#ifdef CAS_IMP_YIELD

         sum_with_cas_imp_yield(NULL);

        #endif

};

int main()

{      

        pthread_t* thread = (pthread_t*) malloc(10*sizeof( pthread_t));

        for(int i&#61;0;i<10;&#43;&#43;i){      

                pthread_create(&thread[i],NULL,sum,NULL);

        }

        for(int i&#61;0;i<10;&#43;&#43;i){      

                pthread_join(thread[i],NULL);

        }

        printf("g_uCount:%d/n",g_uCount);

}

用以下编译命令编译出6个程序

g&#43;&#43; test.cpp -o test_free -D FREE -lpthread

g&#43;&#43; test.cpp -o test_fetchandadd -D FETCH_AND_ADD -lpthread

g&#43;&#43; test.cpp -o test_mlock -D M_LOCK -lpthread

g&#43;&#43; test.cpp -o test_cas -D CAS -lpthread

g&#43;&#43; test.cpp -o test_cas_imp -D CAS_IMP –lpthread

g&#43;&#43; test.cpp –o test_cas_imp_yield –D CAS_IMP_YIELD -lpthread

推荐阅读
  • C语言编写线程池的简单实现方法
    2019独角兽企业重金招聘Python工程师标准好文章,一起分享——有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带 ... [详细]
  • 题目描述:给定一个区间,支持两种操作:1. 将位置a的值修改为b;2. 查询区间[a, b]内的子序列的最大和,其中子序列中相邻的元素必须具有不同的奇偶性。 ... [详细]
  • 普通树(每个节点可以有任意数量的子节点)级序遍历 ... [详细]
  • T15483.【清华集训2017模拟11.26】简单路径T25484.【清华集训2017模拟11.26】快乐树T35485.【清华集训2017模拟11.26】字符串T1结论题,结论很 ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • 基于bionic c分析线程的一生
    1.概述和问题进程和线程操作系统基础和重要的机制,从源码角度理解进程和线程的区别对于理解操作系统的基本原理非常有帮助,同时进程和线程的创建又是通过系统 ... [详细]
  • C语言是计算机科学和编程领域的基石,许多初学者在学习过程中会感到困惑。本文将详细介绍C语言的基本概念、关键语法和实用示例,帮助你快速上手C语言。 ... [详细]
  • 【线段树】  本质是二叉树,每个节点表示一个区间[L,R],设m(R-L+1)2(该处结果向下取整)左孩子区间为[L,m],右孩子区间为[m ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 题目《BZOJ2654: Tree》的时间限制为30秒,内存限制为512MB。该问题通过结合二分查找和Kruskal算法,提供了一种高效的优化解决方案。具体而言,利用二分查找缩小解的范围,再通过Kruskal算法构建最小生成树,从而在复杂度上实现了显著的优化。此方法不仅提高了算法的效率,还确保了在大规模数据集上的稳定性能。 ... [详细]
  • 在操作系统中,阻塞状态与挂起状态有着显著的区别。阻塞状态通常是指进程因等待某一事件(如I/O操作完成)而暂时停止执行,而挂起状态则是指进程被系统暂时移出内存,以释放资源或降低系统负载。此外,本文还深入分析了`sleep()`函数的实现机制,探讨了其在不同操作系统中的具体实现方式及其对进程调度的影响。通过这些分析,读者可以更好地理解操作系统如何管理进程的不同状态以及`sleep()`函数在其中的作用。 ... [详细]
  • ubuntu下基于c++的opencv学习
    一、环境配置1、安装opencv2、makefile编写makefile模板,与c文件在同一个目录下,用make指令生成可执行文件,然后运 ... [详细]
author-avatar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有