C语言多线程简单运算不加锁实现
  cPKDMEpsofBP 2023年11月02日 48 0

C语言多线程简单运算不加锁实现

因为c语言本事并没有提供原子性操作的函数,而使用多线程库当中的锁机制又会大大影响效率,在经过多方查找,以及redis源码当中实现计数器的实现了解到了编译器自带的十二个内置原子性操作函数

原子性操作函数:

//返回更新前的值
type __sync_fetch_and_add (type *ptr, type value, ...)		//自加
type __sync_fetch_and_sub (type *ptr, type value, ...)		//自减
type __sync_fetch_and_or (type *ptr, type value, ...)		//或运算
type __sync_fetch_and_and (type *ptr, type value, ...)		//与运算
type __sync_fetch_and_xor (type *ptr, type value, ...)		//异或操作
type __sync_fetch_and_nand (type *ptr, type value, ...)		//非与

//返回更新后的值
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)

此外还有三个非运算相关的操作函数:

  1. __sync_bool_compare_and_swap:原子性地比较变量的值与预期值,如果相等则将新值赋给变量。
  2. __sync_val_compare_and_swap:原子性地比较变量的值与预期值,如果相等则将新值赋给变量,并返回操作之前的值。
  3. __sync_lock_test_and_set:原子性地设置变量的值,并返回操作之前的值,通常用于实现互斥锁

使用方法:

以自加为例:

#include <stdio.h>

int main() {
    int counter = 0;

    // 使用__sync_fetch_and_add原子性地递增counter的值
    int previous_value = __sync_fetch_and_add(&counter, 1);

    printf("Previous Value: %d\n", previous_value);
    printf("New Value: %d\n", counter);

    return 0;
}

原理:因为该函数本质还是使用的锁机制,但是在汇编当中可以看到,被加锁的指令极少,仅有两三条指令,不同于标准库当中的互斥锁采用的是直接锁住相关的整片内存

C语言多线程简单运算不加锁实现_i++

使用互斥锁机制,在四个线程下累加400000需要0.019015s左右

而在使用编译器自带的原子性操作函数累加400000次仅需0.004416s左右,提升近五倍左右效率

C语言多线程简单运算不加锁实现_i++_02

完整测试代码

#include <stdio.h>
#include<thread>
#include<mutex>
#include<sys/time.h>

#define ___TIME_CLOCK_DECLARE \
        double t_start_time, t_end_time;
#define ___TIME_CLOCK_START  {\
                struct timeval t; gettimeofday(&t, 0);\
                t_start_time = t.tv_sec + 1E-6 * t.tv_usec; \
        }
#define ___TIME_CLOCK_STOP { \
                struct timeval t; gettimeofday(&t, 0); \
                t_end_time = t.tv_sec + 1E-6 * t.tv_usec; \
        }
#define ___TIME_CLOCK_TOTAL (t_end_time - t_start_time)
using namespace std;
int sum=0;
int counter = 0;
std::mutex lo;
void test1(){
    for(int i=0;i<100000;i++){
        // lo.lock();
        // sum+=1;
        // lo.unlock();
        __sync_fetch_and_add(&counter, 1);
    }
}
void test2(){
    for(int i=0;i<100000;i++){
        // lo.lock();
        // sum+=1;
        // lo.unlock();
        __sync_fetch_and_add(&counter, 1);
    }
}
void test3(){
    for(int i=0;i<100000;i++){
        // lo.lock();
        // sum+=1;
        // lo.unlock();
        __sync_fetch_and_add(&counter, 1);
    }
}
void test4(){
    for(int i=0;i<100000;i++){
        // lo.lock();
        // sum+=1;
        // lo.unlock();
        __sync_fetch_and_add(&counter, 1);
    }
}
int main() {

    ___TIME_CLOCK_DECLARE
    ___TIME_CLOCK_START
    std::thread p(test1);
    std::thread p2(test2);
    std::thread p3(test3);
    std::thread p4(test4);

    p.join();
    p2.join();
    p3.join();
    p4.join();
    ___TIME_CLOCK_STOP
    printf("sum:%d  time:%lf\n",counter,___TIME_CLOCK_TOTAL);
    return 0;
}

(为了编写代码方便,部分地方采用了c++写法,但是不影响测试结果数据)

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  gBkHYLY8jvYd   2023年12月06日   50   0   0 #includecii++
  gBkHYLY8jvYd   2023年12月09日   30   0   0 cii++数据
  gBkHYLY8jvYd   2023年12月06日   24   0   0 cii++依赖关系
  lh6O4DgR0ZQ8   2023年11月24日   18   0   0 cii++c++
  gBkHYLY8jvYd   2023年12月10日   19   0   0 #include邻域灰度图像
  gBkHYLY8jvYd   2023年12月10日   24   0   0 #include数组i++
  gBkHYLY8jvYd   2023年12月06日   23   0   0 #includeios数据
  gBkHYLY8jvYd   2023年12月08日   21   0   0 #includecii++
  gBkHYLY8jvYd   2023年11月22日   25   0   0 #include十进制高精度
  gBkHYLY8jvYd   2023年11月22日   26   0   0 #includeiosci