原子操作 (基于Linux 应用层 C 语言)

原子操作的核心是:一个操作在执行过程中不会被其他线程打断,要么完全执行完,要么完全不执行,能解决多线程并发下的竞态问题(比如多个线程同时修改同一个变量导致数据错乱)。

Linux 应用层常用的原子操作接口是 stdatomic.h (C11 标准)提供的原子类型和操作函数,下面通过「普通变量并发修改」和「原子变量并发修改」的对比 Demo,直观展示原子操作的作用。

完整代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <unistd.h>

// 1. 普通全局变量(非原子)
int normal_counter = 0;
// 2. 原子全局变量(C11 标准,Linux 应用层推荐)
atomic_int atomic_counter = ATOMIC_VAR_INIT(0);

// 线程函数:对普通变量进行 10000 次自增(无保护)
void *normal_incr(void *arg) {
    for (int i = 0; i < 10000; i++) {
        // 普通自增:拆分为 "读-改-写" 三步,可能被打断
        normal_counter++;
    }
    pthread_exit(NULL);
}

// 线程函数:对原子变量进行 10000 次自增(原子操作)
void *atomic_incr(void *arg) {
    for (int i = 0; i < 10000; i++) {
        // 原子自增:一步完成,不会被打断
        atomic_fetch_add(&atomic_counter, 1);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t tid1, tid2, tid3, tid4;

    // ========== 测试普通变量(非原子) ==========
    // 创建 2 个线程同时修改普通变量
    pthread_create(&tid1, NULL, normal_incr, NULL);
    pthread_create(&tid2, NULL, normal_incr, NULL);
    // 等待线程结束
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    printf("普通变量(非原子)最终值:%d(预期 20000)\n", normal_counter);

    // ========== 测试原子变量 ==========
    // 创建 2 个线程同时修改原子变量
    pthread_create(&tid3, NULL, atomic_incr, NULL);
    pthread_create(&tid4, NULL, atomic_incr, NULL);
    // 等待线程结束
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);
    printf("原子变量最终值:%d(预期 20000)\n", atomic_load(&atomic_counter));

    return 0;
}

编译 & 运行命令

c 复制代码
# 编译(需要链接 pthread 库)
gcc atomic_demo.c -o atomic_demo -lpthread -std=c11
# 运行
./atomic_demo

运行结果示例

复制代码
普通变量(非原子)最终值:18765(预期 20000)
原子变量最终值:20000(预期 20000)

(注:普通变量的结果每次运行都可能不同,且小于 20000;原子变量的结果永远是 20000)

核心代码解释

  1. 原子变量定义

    ini 复制代码
    atomic_int atomic_counter = ATOMIC_VAR_INIT(0);

    atomic_int 是 C11 定义的原子整型,ATOMIC_VAR_INIT(0) 是原子变量的初始化宏,确保初始化过程也是原子的。

  2. 原子自增操作

    scss 复制代码
    atomic_fetch_add(&atomic_counter, 1);

    atomic_fetch_add 是原子加法函数,作用是把 atomic_counter 的值加 1,整个过程「不可打断」。

    对比普通自增 normal_counter++:它会被拆分为「读取当前值 → 加 1 → 写回」三步,若线程在这三步之间被切换,就会导致数据丢失(比如两个线程同时读到底值 100,都加 1 写回 101,本该加 2 却只加了 1)。

  3. 原子读取操作

    scss 复制代码
    atomic_load(&atomic_counter)

    读取原子变量的值也需要用原子接口,确保读取到的是「最新的、完整的」值。


总结

  1. 原子操作的核心:把「读-改-写」这类多步操作变成「不可打断的一步操作」,解决多线程并发修改变量的竞态问题。
  2. Linux 应用层使用方式 :基于 C11 的 stdatomic.h 实现(无需依赖内核接口),常用接口包括 atomic_fetch_add(原子加)、atomic_fetch_sub(原子减)、atomic_store(原子写)、atomic_load(原子读)等。
  3. 适用场景:简单的数值型变量并发修改(如计数器、标志位),复杂场景(如结构体修改)仍需用互斥锁(pthread_mutex)。
相关推荐
kymjs张涛17 小时前
OpenClaw 学习小组:初识
android·linux·人工智能
程序设计实验室19 小时前
经历分享,发现挖矿木马后,服务器快速备份与重装(腾讯云平台)
linux
Miku1621 小时前
OpenClaw-Linux+飞书官方Plugin安装指南
linux·人工智能·agent
Miku1621 小时前
OpenClaw 接入 QQ Bot 完整实践指南
linux·人工智能·agent
Yogurt_cry1 天前
[树莓派4B] 闲置近10年的爱普生 L310 打印机爆改无线打印机
linux·物联网·树莓派
Johny_Zhao2 天前
OpenClaw中级到高级教程
linux·人工智能·信息安全·kubernetes·云计算·yum源·系统运维·openclaw
Sheffield3 天前
Docker的跨主机服务与其对应的优缺点
linux·网络协议·docker
Sheffield3 天前
Alpine是什么,为什么是Docker首选?
linux·docker·容器