C语言原子操作的实现示例

 更新时间:2025年12月18日 11:12:43   作者:山,离天三尺三  
本文主要介绍了C语言中原子操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

原子操作是并发编程中的一个核心概念。让我详细解释:

什么是原子操作?

原子操作是指在执行过程中不会被中断的操作,要么完全执行,要么完全不执行,不会出现部分执行的状态。

比喻理解

就像原子是不可分割的基本单位一样,原子操作是不可分割的操作单位。

非原子操作的问题

先看一个非原子操作的例子:

int counter = 0;

// 这不是原子操作!
void increment() {
    counter = counter + 1;
}

在汇编层面,这通常分为三步:

mov eax, [counter]  ; 1. 读取counter到寄存器
inc eax             ; 2. 寄存器值加1
mov [counter], eax  ; 3. 写回内存

竞态条件问题

线程A:读取counter=0
线程B:读取counter=0  
线程A:计算0+1=1
线程B:计算0+1=1
线程A:写入counter=1
线程B:写入counter=1  ← 结果应该是2,但实际是1!

C语言中的原子操作

C11标准引入的原子类型

#include <stdatomic.h>

// 声明原子变量
atomic_int atomic_counter = ATOMIC_VAR_INIT(0);

// 原子操作
void increment_atomic() {
    atomic_fetch_add(&atomic_counter, 1);  // 原子加法
}

int main() {
    atomic_counter = 5;                    // 原子存储
    int value = atomic_counter;            // 原子加载
    printf("Counter: %d\n", value);
    
    return 0;
}

常见的原子操作函数

基本操作

#include <stdatomic.h>

atomic_int counter = ATOMIC_VAR_INIT(0);

// 加载和存储
int load_value = atomic_load(&counter);        // 原子读取
atomic_store(&counter, 42);                    // 原子写入

// 交换操作
int old_value = atomic_exchange(&counter, 100); // 原子交换

// 比较交换(CAS - Compare And Swap)
int expected = 100;
if (atomic_compare_exchange_strong(&counter, &expected, 200)) {
    printf("CAS成功: 旧值=%d, 新值=200\n", expected);
}

算术运算

// 原子加法
int old = atomic_fetch_add(&counter, 5);    // counter += 5,返回旧值

// 原子减法  
atomic_fetch_sub(&counter, 3);              // counter -= 3

// 原子自增/自减
atomic_fetch_add(&counter, 1);              // counter++
atomic_fetch_sub(&counter, 1);              // counter--

位运算

atomic_int flags = ATOMIC_VAR_INIT(0);

// 原子位操作
atomic_fetch_or(&flags, 0x01);     // flags |= 0x01
atomic_fetch_and(&flags, ~0x01);   // flags &= ~0x01
atomic_fetch_xor(&flags, 0x03);    // flags ^= 0x03

原子操作的实际例子

1. 无锁计数器

#include <stdatomic.h>
#include <pthread.h>
#include <stdio.h>

atomic_int counter = ATOMIC_VAR_INIT(0);

void* worker(void* arg) {
    for (int i = 0; i < 100000; i++) {
        atomic_fetch_add(&counter, 1);  // 原子自增
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    
    pthread_create(&t1, NULL, worker, NULL);
    pthread_create(&t2, NULL, worker, NULL);
    
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    
    printf("最终计数: %d (应该是200000)\n", atomic_load(&counter));
    return 0;
}

2. 自旋锁实现

#include <stdatomic.h>

typedef atomic_flag spinlock_t;

void spinlock_init(spinlock_t* lock) {
    atomic_flag_clear(lock);
}

void spinlock_lock(spinlock_t* lock) {
    // 忙等待,直到获得锁
    while (atomic_flag_test_and_set(lock)) {
        // 可选的:减少CPU占用
        // __builtin_ia32_pause();  // x86的PAUSE指令
    }
}

void spinlock_unlock(spinlock_t* lock) {
    atomic_flag_clear(lock);
}

3. 无锁栈(Lock-Free Stack)

#include <stdatomic.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node* next;
} Node;

typedef struct {
    _Atomic(Node*) top;
} LockFreeStack;

void stack_init(LockFreeStack* stack) {
    atomic_store(&stack->top, NULL);
}

void stack_push(LockFreeStack* stack, int value) {
    Node* new_node = malloc(sizeof(Node));
    new_node->data = value;
    
    Node* old_top;
    do {
        old_top = atomic_load(&stack->top);
        new_node->next = old_top;
    } while (!atomic_compare_exchange_weak(&stack->top, &old_top, new_node));
}

int stack_pop(LockFreeStack* stack) {
    Node* old_top;
    Node* new_top;
    
    do {
        old_top = atomic_load(&stack->top);
        if (old_top == NULL) return -1;  // 栈空
        new_top = old_top->next;
    } while (!atomic_compare_exchange_weak(&stack->top, &old_top, new_top));
    
    int value = old_top->data;
    free(old_top);
    return value;
}

内存顺序(Memory Order)

原子操作还涉及内存可见性问题:

#include <stdatomic.h>

atomic_int data = ATOMIC_VAR_INIT(0);
atomic_int flag = ATOMIC_VAR_INIT(0);

// 生产者线程
void producer() {
    atomic_store_explicit(&data, 42, memory_order_relaxed);
    atomic_store_explicit(&flag, 1, memory_order_release);  // 释放语义
}

// 消费者线程
void consumer() {
    while (atomic_load_explicit(&flag, memory_order_acquire) == 0) {
        // 等待
    }
    int value = atomic_load_explicit(&data, memory_order_relaxed);
    printf("Data: %d\n", value);  // 保证看到42
}

不同平台的原生原子操作

x86架构

// 内联汇编实现原子操作
int atomic_increment(int* value) {
    __asm__ __volatile__(
        "lock incl %0"          // lock前缀确保原子性
        : "+m" (*value)
        :
        : "cc"
    );
    return *value;
}

GCC内置原子操作

int counter = 0;

// GCC内置的原子操作
void increment_gcc() {
    __sync_fetch_and_add(&counter, 1);
}

int compare_and_swap_gcc(int* ptr, int oldval, int newval) {
    return __sync_val_compare_and_swap(ptr, oldval, newval);
}

原子操作的优缺点

优点:

  • 高性能:避免锁的开销
  • 无死锁:不会出现锁顺序问题
  • 可扩展性:在多核系统中表现良好

缺点:

  • 复杂性:正确实现很困难
  • ABA问题:在CAS操作中可能出现
  • 平台依赖性:不同硬件支持程度不同

原子操作 vs 互斥锁

特性原子操作互斥锁
性能高(硬件支持)中等(系统调用)
复杂度
适用场景简单操作(计数器、标志位)复杂临界区
死锁风险
可扩展性一般

总结

原子操作是:

  • 不可分割的操作单位
  • 线程安全的,无需额外同步
  • 硬件支持的,通常通过CPU指令实现
  • 高性能的并发编程基础

在现代多核处理器系统中,原子操作是实现高效并发程序的重要工具,特别是在实现无锁数据结构、计数器、标志位等场景中。

到此这篇关于C语言原子操作的实现示例的文章就介绍到这了,更多相关C语言 原子操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中volatile关键字的使用详解以及常见的误解

    C++中volatile关键字的使用详解以及常见的误解

    volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统,硬件或者其他线程等
    2020-01-01
  • C语言二维数组应用之井字棋游戏

    C语言二维数组应用之井字棋游戏

    这篇文章主要为大家详细介绍了C语言二维数组应用之井字棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C语言从strcpy到自定义字符串处理函数的原理解析

    C语言从strcpy到自定义字符串处理函数的原理解析

    这篇文章主要介绍了C语言从strcpy到自定义字符串处理函数的相关资料,本文将从常见的字符串函数(如strcpy、strcat)入手,逐步深入探讨字符串操作的核心原理,并引导你实现自定义字符串处理函数,感兴趣的朋友一起看看吧
    2025-05-05
  • Matlab实现绘制有气泡感的网络图

    Matlab实现绘制有气泡感的网络图

    这篇文章主要介绍了如何利用Matlab实现绘制有气泡感的网络图,文中的示例代码讲解详细,对我们学习Matlab有一定的帮助,需要的可以参考一下
    2023-02-02
  • C++中String类型的逆序方式

    C++中String类型的逆序方式

    这篇文章主要介绍了C++中String类型的逆序方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Qt 智能指针QScopedPoint用法小结

    Qt 智能指针QScopedPoint用法小结

    智能指针是C++11引入的一种指针封装类型,用于自动管理动态分配的内存,本文主要介绍了Qt 智能指针QScopedPoint用法小结,感兴趣的可以了解一下
    2024-01-01
  • C语言实现图书管理系统

    C语言实现图书管理系统

    这篇文章主要为大家详细介绍了C语言实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C语言 野指针与空指针专篇解读

    C语言 野指针与空指针专篇解读

    全网最接地气的C语言野指针介绍,此处对于野指针与空指针知识点做一些简要的介绍,作者实属初学,写博客也是作者学习的一个过程,难免文章中有内容理解不到位或者有不当之处,还请朋友们不吝指正,希望大家多多给予支持,赠人玫瑰,手有余香
    2021-11-11
  • C语言对于volatile与gcc优化的探究

    C语言对于volatile与gcc优化的探究

    这篇文章主要介绍了C语言对于volatile与gcc优化的探究,volatile是一个特征修饰符(type specifier) volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。这是百度百科的介绍,那编译器是具体是怎么优化的呢
    2023-02-02
  • 详解C语言gets()函数与它的替代者fgets()函数

    详解C语言gets()函数与它的替代者fgets()函数

    这篇文章主要介绍了详解C语言gets()函数与它的替代者fgets()函数的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-10-10

最新评论