《并发编程核心机制系列(一):Linux原子操作API完整指南》

《并发编程核心机制系列(一):Linux原子操作API完整指南》

在多线程/多进程编程中,原子操作是一个非常重要的概念。本文将通过详细的API说明和实例,帮助你掌握Linux原子操作的使用。

一、基础概念

原子操作就像是一个不可分割的整体,执行过程中不会被打断。比如多个线程同时给一个计数器加1,如果不使用原子操作,最终结果可能会出错。

二、原子整数操作API详解

1. 初始化相关API

// 1. 静态初始化
atomic_t counter = ATOMIC_INIT(0);    // 编译时初始化

// 2. 动态初始化
atomic_t dynamic_counter;
atomic_set(&dynamic_counter, 100);    // 运行时设置值

// 3. 读取值
int value = atomic_read(&counter);    // 原子地读取值

// 使用示例
void counter_init_example(void) {
    atomic_t users = ATOMIC_INIT(0);
    atomic_set(&users, 10);
    printf("当前用户数: %d\n", atomic_read(&users));
}

2. 基本运算API

// 1. 加法操作族
void atomic_add(int i, atomic_t *v);         // 加上i
int atomic_add_return(int i, atomic_t *v);   // 加上i并返回新值
void atomic_inc(atomic_t *v);                // 加1
int atomic_inc_return(atomic_t *v);          // 加1并返回新值

// 2. 减法操作族
void atomic_sub(int i, atomic_t *v);         // 减去i
int atomic_sub_return(int i, atomic_t *v);   // 减去i并返回新值
void atomic_dec(atomic_t *v);                // 减1
int atomic_dec_return(atomic_t *v);          // 减1并返回新值

// 使用示例
void atomic_ops_example(void) {
    atomic_t count;
    atomic_set(&count, 5);
    
    // 加法操作
    atomic_add(3, &count);                   // count = 8
    int after_add = atomic_add_return(2, &count);  // count = 10
    printf("加法后的值: %d\n", after_add);    // 输出:10
    
    // 减法操作
    atomic_sub(4, &count);                   // count = 6
    int after_dec = atomic_dec_return(&count);     // count = 5
    printf("减法后的值: %d\n", after_dec);    // 输出:5
}

3. 条件操作API

// 1. 测试操作族
int atomic_dec_and_test(atomic_t *v);   // 减1并测试是否为0
int atomic_inc_and_test(atomic_t *v);   // 加1并测试是否为0
int atomic_sub_and_test(int i, atomic_t *v);  // 减去i并测试是否为0
int atomic_add_negative(int i, atomic_t *v);   // 加上i并测试是否为负

// 2. 比较和交换操作
int atomic_cmpxchg(atomic_t *v, int old, int new);  // 比较并交换
int atomic_xchg(atomic_t *v, int new);              // 直接交换

// 使用示例
void atomic_cond_example(void) {
    atomic_t value;
    atomic_set(&value, 1);
    
    // 减1并测试
    if (atomic_dec_and_test(&value)) {
        printf("值已变为0\n");
    }
    
    // 比较并交换示例(简单的锁实现)
    if (atomic_cmpxchg(&value, 0, 1) == 0) {
        printf("获得了锁\n");
        // ... 临界区代码 ...
        atomic_set(&value, 0);  // 释放锁
    }
}

三、原子位操作API详解

1. 基本位操作

// 1. 基本位操作族
void set_bit(int nr, volatile unsigned long *addr);    // 设置位
void clear_bit(int nr, volatile unsigned long *addr);  // 清除位
void change_bit(int nr, volatile unsigned long *addr); // 翻转位
int test_bit(int nr, const volatile unsigned long *addr);  // 测试位

// 2. 测试并操作族
int test_and_set_bit(int nr, volatile unsigned long *addr);    // 测试并设置
int test_and_clear_bit(int nr, volatile unsigned long *addr);  // 测试并清除
int test_and_change_bit(int nr, volatile unsigned long *addr); // 测试并翻转

// 使用示例
void bit_ops_example(void) {
    unsigned long flags = 0;
    
    // 设置和测试位
    set_bit(0, &flags);                  // 设置第0位
    if (test_bit(0, &flags)) {
        printf("第0位已设置\n");
    }
    
    // 测试并设置
    if (!test_and_set_bit(1, &flags)) {
        printf("第1位原来为0,现在已设置为1\n");
    }
    
    // 清除位
    clear_bit(0, &flags);
    if (!test_bit(0, &flags)) {
        printf("第0位已清除\n");
    }
}

四、实际应用示例

1. 引用计数器实现

struct shared_resource {
    atomic_t ref_count;
    void *data;
};

// 创建资源
struct shared_resource* resource_create(void) {
    struct shared_resource *res = malloc(sizeof(*res));
    if (res) {
        atomic_set(&res->ref_count, 1);  // 初始引用计数为1
        res->data = NULL;
    }
    return res;
}

// 增加引用
void resource_get(struct shared_resource *res) {
    atomic_inc(&res->ref_count);
}

// 释放引用
void resource_put(struct shared_resource *res) {
    if (atomic_dec_and_test(&res->ref_count)) {
        // 引用计数变为0,释放资源
        free(res->data);
        free(res);
    }
}

// 使用示例
void reference_count_example(void) {
    struct shared_resource *res = resource_create();
    
    // 增加引用
    resource_get(res);
    
    // 使用资源
    // ... 
    
    // 释放引用
    resource_put(res);
}

2. 简单自旋锁实现

typedef struct {
    atomic_t lock;
} spinlock_t;

// 初始化自旋锁
void spin_lock_init(spinlock_t *lock) {
    atomic_set(&lock->lock, 0);
}

// 获取锁
void spin_lock(spinlock_t *lock) {
    while (atomic_cmpxchg(&lock->lock, 0, 1) != 0) {
        cpu_relax();  // 优化自旋等待
    }
}

// 释放锁
void spin_unlock(spinlock_t *lock) {
    atomic_set(&lock->lock, 0);
}

// 使用示例
void spinlock_example(void) {
    spinlock_t lock;
    spin_lock_init(&lock);
    
    // 进入临界区
    spin_lock(&lock);
    // ... 临界区代码 ...
    spin_unlock(&lock);
}

3. 并发计数器

struct concurrent_counter {
    atomic_t value;
    atomic_t overflow_count;
};

// 初始化计数器
void counter_init(struct concurrent_counter *counter) {
    atomic_set(&counter->value, 0);
    atomic_set(&counter->overflow_count, 0);
}

// 增加计数
void counter_inc(struct concurrent_counter *counter) {
    if (atomic_inc_return(&counter->value) < 0) {
        // 发生溢出,增加溢出计数
        atomic_inc(&counter->overflow_count);
        atomic_set(&counter->value, 0);
    }
}

// 获取总计数
unsigned long get_total_count(struct concurrent_counter *counter) {
    return (unsigned long)atomic_read(&counter->overflow_count) * INT_MAX +
           atomic_read(&counter->value);
}

五、使用注意事项

  1. 性能考虑

    • 原子操作比普通操作慢
    • 避免频繁的原子操作
    • 合理使用内存屏障
  2. 使用场景选择

    • 简单的计数场景
    • 状态标志管理
    • 引用计数
    • 简单的同步机制
  3. 调试技巧

    • 使用原子操作的返回值进行验证
    • 合理设置断言检查
    • 记录关键状态变化

总结

Linux原子操作API提供了一套完整的原子操作机制,通过合理使用这些API,我们可以实现高效的并发控制。在实际开发中,需要根据具体场景选择合适的API,并注意性能和正确性的平衡。


作者:Morgan
这是我的学习笔记,记录了对Linux原子操作API的深入理解。
持续学习,不断进步!

Lumière Notes - 用代码点亮前行之路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值