__sync_fetch_and_add函数(Redis源码学习)

12027.linux flash驱动之IS25LP01G/IS25WWP01G 一页:512字节 --> 扇区 4096字节 --> 块 32k,64k, 层层递进关系。 阅读详情

__sync_fetch_and_add函数(Redis源码学习)

在学习redis-3.0源码中的sds文件时,看到里面有如下的C代码,之前从未接触过,所以为了全面学习redis源码,追根溯源,学习一下__sync_fetch_and_add的系列函数:

#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))

在网上查找相关 __sync_add_and_fetch 函数的知识点,基本都是一样的内容,于是总结如下。

1.背景由来

实现多线程环境下的计数器操作,统计相关事件的次数. 当然我们知道,count++这种操作不是原子的。一个自加操作,本质是分成三步的:

 1 从缓存取到寄存器
 2 在寄存器加1
 3 存入缓存。

由于时序的因素,多个线程操作同一个全局变量,会出现问题。这也是并发编程的难点。在目前多核条件下,这种困境会越来越彰显出来。
最简单的处理办法就是加锁保护,这也是我最初的解决方案。看下面的代码:

    pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&count_lock);
    global_int++;
    pthread_mutex_unlock(&count_lock);

后来在网上查找资料,找到了__sync_fetch_and_add系列的命令,相关英文文章: Multithreaded simple data type access and atomic variables,

2.系列函数

__sync_fetch_and_add系列一共有十二个函数,有加/减/与/或/异或/等函数的原子性操作函数,__sync_fetch_and_add,顾名思义,先fetch,然后自加,返回的是自加以前的值。以count = 4为例,调用__sync_fetch_and_add(&count,1)之后,返回值是4,然后,count变成了5.

简单验证代码如下sync_fetch_add.c:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
    int count = 4;
    printf("111 count:%d\n",count);
    int retval = __sync_fetch_and_add(&count,10);

    printf("222 retval:%d\n",retval);
    printf("222 count:%d\n",count);

    return 0;
}

linux 系统中命令行执行:gdb -g -o sync_fetch_add sync_fetch_add.c

得到可执行文件,执行后得到如下结果:

./sync_fetch_add 
111 count:4
222 retval:4
222 count:14

其他函数可以自行验证。

有__sync_fetch_and_add,自然也就有__sync_add_and_fetch,呵呵这个的意思就很清楚了,先自加,在返回。他们的关系与i++和++i的关系是一样的。有了这个函数,对于多线程对全局变量进行自加,我们就再也不用理线程锁了。下面这行代码,和上面被pthread_mutex保护的那行代码作用是一样的,而且也是线程安全的。

在用gcc编译的时候要加上选项 -march=i686,我在执行上面代码时,gcc没加该参数,使用到的版本gcc version 4.4.7 20120313 , 上面代码能正常运行通过。

下面是这群函数的全部,无非是先fetch再运算,或者先运算再fetch。

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);

GCC 提供的原子操作
gcc从4.1.2提供了__sync_*系列的built-in函数,用于提供加减和逻辑运算的原子操作。

其声明如下:

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, ...)

这两组函数的区别在于第一组返回更新前的值,第二组返回更新后的值。

看网上有大师的代码测试例子Alexander Sandler,现拷贝为 sync_fetch2.c 文件如下并验证执行结果:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <errno.h>

#define INC_TO 1000000 // one million...

int global_int = 0;
  
pid_t gettid( void )
{
	return syscall( __NR_gettid );
}

void *thread_routine( void *arg )
{
	int i;
	int proc_num = (int)(long)arg;
	cpu_set_t set;

	CPU_ZERO( &set );
	CPU_SET( proc_num, &set );

	if (sched_setaffinity( gettid(), sizeof( cpu_set_t ), &set ))
	{
		perror( "sched_setaffinity" );
		return NULL;
	}

	for (i = 0; i < INC_TO; i++)
	{
		// global_int++;
		__sync_fetch_and_add( &global_int, 1 );
	}

	return NULL;
}

int main()
{
	int procs = 0;
	int i;
	pthread_t *thrs;    

	// Getting number of CPUs
	procs = (int)sysconf( _SC_NPROCESSORS_ONLN );
	if (procs < 0)
	{
		perror( "sysconf" );
		return -1;
	}

	thrs = (pthread_t *)malloc( (sizeof( pthread_t )) * procs );
	if (thrs == NULL)
	{
		perror( "malloc" );
		return -1;
	}

	printf( "Starting %d threads...\n", procs );

	for (i = 0; i < procs; i++)
	{
		if (pthread_create( &thrs[i], NULL, thread_routine,
			(void *)(long)i ))
		{
			perror( "pthread_create" );
			procs = i;
			break;
		}
	}

	for (i = 0; i < procs; i++)
		pthread_join( thrs[i], NULL );

	free( thrs );

	printf( "After doing all the math, global_int value is: %d\n",global_int );
	printf( "Expected value is: %d\n", INC_TO * procs );

	return 0;
}

上面代码在RHEL6.9中编译:g++ -g -o sync_fetch2 sync_fetch2.c -lpthread
执行结果为:

./sync_fetch2 
Starting 4 threads...
After doing all the math, global_int value is: 4000000
Expected value is: 4000000

如果将上面thread_routine函数中的这两句换一下,直接用变量加加,则每次执行都得到不一样的值

	global_int++;
	// __sync_fetch_and_add( &global_int, 1 );

修改后得到结果如下:

$./sync_fetch2                                
Starting 4 threads...
After doing all the math, global_int value is: 1428371
Expected value is: 4000000

$ ./sync_fetch2 
Starting 4 threads...
After doing all the math, global_int value is: 2479197
Expected value is: 4000000

3.小结

可以从代码验证中看到 __sync_fetch_and_add 函数的作用,在多线程中,对简单的变量运算能保证结果的正确,至于其他函数,参考上面代码,读者可以自行验证。

另外基于上面例子,有人修改代码,加上执行消耗时间,通过__sync_fetch_and_add和加锁机制的对比,发现__sync_fetch_and_add比加解锁机制快了6-7倍,执行速度还是很快的,因为涉及到汇编代码,后续有机会会再学习验证。

本人才疏学浅,文章错误不当之处,请批评指正。如果文章对您有一点点帮助,是我的荣幸,更多内容见公众号:hongmaolinux
红帽linux编程

参考网址:

http://www.alexonlinux.com/multithreaded-simple-data-type-access-and-atomic-variables
https://blog.youkuaiyun.com/i_am_jojo/article/details/7591743
https://www.zhihu.com/question/280022939
https://blog.youkuaiyun.com/long2324066440/article/details/72784084

RT-DETR改进策略【注意力机制篇】| 2024 PPA 并行补丁感知注意模块,提高小目标关注度 本文记录的是利用改进检测精度,详细说明了优化原因,注意事项等。原论文在红外小目标检测任务中,小目标在多次下采样操作中容易丢失关键信息。通过替代编码器和解码器基本组件中的传统卷积操作,更好地保留小目标的重要信息。HCF-Net: Hierarchical Context Fusion Network for Infrared Small Object Detection采用多分支特征提取策略,通过不同分支提取不同尺度和层次的特征。利用局部、全局和串行卷积分支,对输入特征张量进行处理。通过控制 patch si 阅读详情

相关推荐

3分钟!教会你用 DeepSeek/Claude3.7+Cursor 快速入门AI编程(喂饭级教程)

3分钟!教会你用 DeepSeek/Claude3.7+Cursor 快速入门AI编程(喂饭级教程)

yizhen91y的博客 4724

原子计数操作 — _syn_fetch_and_add等12个操作

原子计数操作  — _syn_fetch_and_add等12个操作         多线程计数操作,共享状态或者统计相关时间次数,这些都需要在多线程之间共享变量和修改变量,如此就需要在多线 程间对该变量进行互斥操作和访问.我们谭老先生早都说过了! ++i 和 i++都是不是原子性的,本质上为三步: 1. 从缓存取到寄存器 2. 在寄存器中加1 3. 再存入缓存 但是有时候由于...

阿亮的博客 4410

Windows10 搭建 FTP 服务器

问题: 有这么一个需求,博主需要对班级同学的信息进行收集,同学的信息是一个个的文件, 正常是每个同学将自己的信息打包压缩通过 QQ 或者邮箱方式发送给博主, 博主一个个接收这些压缩包,然后进行解压,再统一放到一个文件夹下, 但是博主觉得这样子实在是太麻烦了,如果某一个同学的信息来回变动,就需要接收很多次,更改很多次, 另外如果消息过多还会产生漏掉某个同学的信息的问题。 解决方法: 如果可以使...

assiduous_me的博客 3965

多线程无锁编程--原子计数操作:__sync_fetch_and_add等12个操作

无锁编程——__sync_fetch_and_add()与线程锁比较。

minCrazy的专栏 8142

从ST电机库到TI InstaSPIN:手把手对比两种主流FOC实现中的坐标变换差异

本文深入对比了ST电机库与TI InstaSPIN在FOC实现中的坐标变换差异,包括坐标系定义、变换系数和角度处理等关键点。针对BLDC和PMSM电机控制,提供了从理论到实践的移植方案,帮助工程师快速解决平台切换中的性能问题。特别解析了Clarke/Park变换在两种实现中的微妙差异及其对PI控制的影响。

weixin_28724661的博客 398

硬件级Dalvik字节码粒度无痕HOOK及VMP取址码定位分析

2.通过硬件读写断点的输出信息 可知pc寄存器就是art解释器的取码地址 直接硬件执行断点在这个pc 即可直接trace所有的smali执行流 通杀所有机型系统。本文即将探讨的是 如何在硬件级别实现dalvik bytecode粒度的无痕拦截技术 并实现鼠标点击一键hook 指哪儿打哪儿 感受 button power 的强大魅力!4.由于硬件断点是同步的 断下后可随时硬件dump下这一瞬间的app内存映像 写入unicorn的内存模拟执行。解释器在解释执行前必会读取内存中的bytecode。

2503_90751789的博客 1085

C\C++:原子计数操作 之__syn_fetch_and_add性能研究

C\C++:原子计数操作 之__syn_fetch_and_add性能研究

hhd1988的专栏 2033

CS算法实战:用MATLAB从零实现SAR点目标成像(附完整代码)

本文详细介绍了如何使用MATLAB从零实现SAR点目标CS成像算法,涵盖Chirp Scaling(CS)算法的核心理论、距离徙动校正(RCMC)和方位压缩等关键步骤。通过完整的代码示例和实战技巧,帮助读者掌握SAR成像处理算法,提升遥感图像处理能力。

weixin_29211309的博客 179

linux无锁化编程--__sync_fetch_and_add系列原子操作函数

__sync_fetch_and_add系列的命令,发现这个系列命令讲的最好的一篇文章,英文好的同学可以直接去看原文。Multithreaded simple data type access and atomic variables __sync_fetch_and_add系列一共有十二个函数,有加/减/与/或/异或/等函数的原子性操作函数,__sync_fetch_and_add,顾名思义,

风叶 7万+

32GB Jetson Orin SOM 不能刷机问题排查

拿到了一个新的Orin SOM,当用orin Devkit CVB或定制的CVB不能用Linux_For_Tegra刷机,flash日志(JetPack5.0DP和5.0.1DP都失败了) 2. 确认mb2配置中将CVB的eeprom读取大小设置是否为0 ? 已经确认是正常的 在定制的orin cvb flash上使用Orin devkit som,启动是成功的。但新得到的Orin som并不工作可以排除理由:使用Orin Devkit CVB和Orin SOM(不是Devkit SOM)仍然不能

jzwjzw19900922的博客 1499

智驾车载以太网(18)-switch之报文转发和vlan处理流程

本文详细解析了交换机中报文处理和转发流程:1)通过目标MAC查找转发端口,源MAC创建ARL条目;2)VLAN标签处理包括tagged/untagged帧判断、默认VID标记等5种处理方式;3)完整转发流程包含CRC校验、IQ封装、VLAN和ARL表查询、端口向量计算及优先级队列发送。文中还涉及TCAM修改VLAN、802.1q多种模式处理等细节,省略了流量控制等次要流程。该过程实现了基于MAC和VLAN的高效二层交换。

mitugaoyang1993的博客 43

关于 线程模型中经常使用的 __sync_fetch_and_add 原子操作的性能

__sync_fetch_and_add lock前缀锁内存总线的性能是远优于 使用 futex 调度线程上下文的性能。

天行健,地势坤 3441

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

深入掌握 go-redis/v9:官方 Go Redis 客户端完整实战指南(含 Loki 仓库实践)

`go-redis`(`github.com/redis/go-redis/v9`)是 Redis 官方为 Go 语言打造的 Redis 客户端库,提供与 Redis 服务器交互的简洁接口,覆盖连接池、管道、事务、Pub/Sub、脚本、Sentinel、Cluster、客户端缓存等完整能力。本文以仓库 [vendor/github.com/redis/go-redis/v9/README.md](

gitblog_00941的博客 258

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

小爱音箱如何刷入开源自定义固件:xiaoai-patch 完整指南

家里的小爱音箱用久了,最直观的痛点是封闭:装不了第三方软件,音乐源被锁在原厂生态里。xiaoai-patch 是一个面向小米小爱音箱的开源自定义固件项目,它通过补丁和自建软件包把音箱系统重做成开源形态,让旧设备变回一台完全由你自己控制的小主机。 [![LX06小爱音箱主板实拍,自定义固件刷机目标硬件](https://raw.gitcode.com/GitHub_Trending/xia/xia

gitblog_00843的博客 791

Happy 多副本架构实战:基于 Socket.IO Redis Streams Adapter 的跨 Pod RPC 与广播路由

本文深入剖析 happy 仓库中 happy-server 如何从单副本演进为多 Kubernetes 副本运行:Socket.IO Redis streams adapter 如何把 `io.to(...).emit(...)` 在副本之间转发,RPC(Web 客户端 → 本地 daemon)如何通过 `rpc:<userId>:<method>` 房间完成跨副本寻址,以及 Pod 被杀、瞬时断

gitblog_00021的博客 341

SVGL API 使用指南:一站式获取 SVG Logo 数据与优化后源码的免费 RESTful 接口

SVGL 项目在仓库内维护了一个庞大的 SVG Logo 库,并配套提供了一组免费、开放、无需认证的 RESTful API(即 SVGL API)。本篇指南以官方 API 参考文档为核心,结合仓库内的 Express 实现源码,完整讲解所有可用端点、TypeScript 类型定义、限流机制与错误返回,帮助你快速将 SVGL 的 Logo 数据接入到扩展、插件或其他工具中。 ## API 概览与

gitblog_00909的博客 676

BullMQ Elixir Job Options 完全指南:从添加到清理的全量配置详解

> **导读**:本指南以 Elixir 版 BullMQ 的 `BullMQ.Queue.add/4` 与 `add_bulk/3` 为切入点,系统讲解任务(Job)的优先级、延迟调度、重试退避、自定义 ID、去重(Deduplication)、LIFO 处理、自动清理等全部配置项,并结合仓库源码(`elixir/lib/bullmq/queue.ex`、`job.ex`、`backoff.ex

gitblog_09743的博客 418

Cortex 中的 Miniredis 实战指南:用纯 Go 内存 Redis 服务器编写可靠的单测

在 Cortex 这类大型分布式时序数据库项目中,许多组件(缓存、索引、限流等)都依赖 Redis。如果每个单测都要启动一个真实的 Redis 实例,测试会变得缓慢、脆弱且难以在 CI 中并行。Miniredis 提供了一个纯 Go 实现、无需任何外部二进制依赖、自带真实 TCP 接口的内存 Redis 替代品,让你能在单元测试中廉价地"假装"连上了 Redis。读完本文,你将掌握 minired

gitblog_00940的博客 179

Lettuce Redis Search 实战指南:从全文检索到向量搜索与聚合分析

本篇技术指南以 Lettuce(当前仓库 lettuce-core,Redis Java client)对 Redis Search 的官方支持为主线,系统讲解如何在 Lettuce 6.8 及之后版本中,通过 `RediSearchCommands` 接口使用 Redis 的全文检索、向量搜索、地理查询与聚合分析能力。读完本文,你将掌握索引创建与字段建模、查询语法与参数控制、向量/地理数据写入与

gitblog_00409的博客 435

flexprice Context Sync 实践:基于 git-SHA 水印的 AGENTS.md 上下文同步机制

> 本文讲解 flexprice 仓库内置的 Context Sync(上下文同步)机制:它如何用 git-SHA 作为"水印",持续检测并驱动各层 `AGENTS.md` 与代码库保持同步。读完本文,你将掌握 `sync.py` 的 `--check / --sync / --advance` 三阶段命令流、manifest 注册表的结构与新增层的方法、LLM 更新上下文的最小化原则,以及如何把

gitblog_00949的博客 198

Python 3.8 + Requests 库实战:基于AutoUU源码的自动化上架脚本重构指南

本文详细介绍了如何利用Python 3.8和Requests库重构基于AutoUU源码的自动化上架脚本,重点优化了API集成、模块化设计和错误处理机制。通过工程化改造,显著提升了脚本的稳定性与可维护性,适用于电商自动化运营等场景。

423

BullMQ Elixir 版事务型任务(Transactional Jobs)设计解析:基于 Postgres 后端实现业务逻辑与任务完成同事务提交

本文深入解析 BullMQ Elixir 端口(`elixir/` 目录)中关于 **Postgres 后端事务型任务(Transactional Jobs)** 的架构设计方案([transactional-jobs-design.md](https://link.gitcode.com/i/82d2694f6c4e042945f0d0e2451a9caf))。该方案的核心目标是:让任务处理器(

gitblog_01078的博客 819

Go init函数本质与安全初始化实践指南

Go中的init函数并非传统意义上的初始化方法,而是编译期由运行时强制注入的包级执行钩子,其执行顺序严格遵循导入依赖拓扑与文件声明序。它不参与运行时调度、不可显式调用、无法错误捕获,因而具备高度隐式性与潜在危险性。理解init的编译期契约特性,是规避I/O阻塞、goroutine死锁、并发写冲突及panic失控等高危问题的前提。在微服务、配置中心、数据库驱动等典型场景中,不当使用init极易引发启动失败、测试不可靠与线上故障。本文基于真实生产案例,系统解析init原理,并提供可测试、可配置、可灰度的显式初始

cuiji1279的博客 405

openJiuwen SkillHub 推荐系统实现解析:Milvus 离线建库与 Redis 在线级联召回实战指南

本文围绕 openJiuwen Agentic Hub(skillhub 仓库)中 `marketplace/recommender/` 实现的个性化推荐系统展开,系统讲解「离线建库 + 在线召回」的完整链路:从 MySQL 资产与行为数据出发,经对象存储拉取 Skill zip、Embedding 向量化写入 Milvus、聚合用户序列与下载 TopK 快照到 Redis,再到在线请求按用户历史

gitblog_00778的博客 344

RQ 任务入队完全指南:从基础入队到依赖、回调、Webhook 与并发限流

RQ(Redis Queue)是一个基于 Redis 的轻量级 Python 任务队列:你只需把一个普通 Python 函数连同参数推入队列,后台的 worker 进程便会异步执行它。本文围绕 [docs/docs/index.md](https://link.gitcode.com/i/b91ddb2f664a9aa754750a1c95c7964b) 这份官方文档展开,完整覆盖入队基础用法、批

gitblog_00288的博客 328

在 Mac 上运行 Windows 程序:Whisky 上手教程

需要跑 Windows 独占的软件,但不想为它开一台虚拟机?Whisky 是 Mac 上的 Wine 图形封装工具,帮你把 .exe 和 .msi 安装程序直接装进 Mac,适合用 Apple Silicon 芯片跑办公、开发工具或老游戏的用户。 ## 一分钟了解 Whisky Whisky 是一个用 SwiftUI 编写的 Wine 图形封装器,底层基于 CrossOver 22.1.1 和

gitblog_00919的博客 977

Happy 实时同步与 RPC 机制全解析:基于 Socket.IO 的 `/v1/updates` 通道设计

Happy 是面向 Codex 与 Claude Code 的移动端与 Web 客户端,其核心难点在于:让 App、Web、CLI 编码会话与后台 Daemon 四类客户端,在加密传输的前提下共享同一份会话、机器与消息状态,并能在设备间发起点对点控制调用。本文以仓库中的 [实时同步与 RPC 顶层文档](https://link.gitcode.com/i/3ea73048076109e0e404

gitblog_01185的博客 212
上一篇: Lua __index __newindex元表方法学习
下一篇: 柔性数组(Redis源码学习)
highmaple
博客等级 码龄16年 0粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值