C++如何使用原子操作_C++11 std::atomic实现无锁编程

std::atomic提供无锁线程同步,通过原子操作如load、store、CAS及内存序控制实现高效并发,适用于计数器、栈等场景,但需注意ABA问题与内存回收风险。

在C++11中,std::atomic 提供了一种类型安全的方式来执行原子操作,避免使用互斥锁实现线程同步,从而提升性能。无锁编程(lock-free programming)利用原子操作保证多线程环境下数据的一致性和可见性,适用于高并发场景。

原子操作的基本概念

原子操作是指不可被中断的操作,要么完全执行,要么不执行,不会出现中间状态。在多线程环境中,对共享变量的读写可能引发竞态条件(race condition),传统做法是用互斥锁保护临界区。而使用 std::atomic 可以在不加锁的情况下安全地访问共享数据。

并不是所有类型都支持原子操作,std::atomic 支持如 int、bool、指针等基础类型。例如:

std::atomic counter{0};
std::atomic ready{false};
std::atomic ptr;

常见原子操作与内存序

std::atomic 支持多种操作,包括 load、store、fetch_add、exchange、compare_exchange_weak/strong 等。这些操作可以配合不同的内存顺序(memory order)来控制同步强度和性能。

常用的内存序包括:

  • memory_order_relaxed:仅保证原子性,不提供同步或顺序约束
  • memory_order_acquire:用于读操作,确保之后的读写不会被重排到该操作之前
  • memory_order_release:用于写操作,确保之前的读写不会被重排到该操作之后
  • memory_order_acq_rel:结合 acquire 和 release
  • memory_order_seq_cst:最严格的顺序一致性,默认选项

示例:递增计数器

std::atomic count{0};

void increment() { count.fetch_add(1, std::memory_order_relaxed); }

使用 compare-and-swap 实现无锁结构

最强大的原子操作之一是 compare_exchange_weakcompare_exchange_strong,它们实现了“比较并交换”(CAS)逻辑,是构建无锁数据结构的基础。

典型用法:

std::atomic value{0};

int expected = value.load(); while (!value.compare_exchange_weak(expected, expected + 1)) { // 如果 value 不等于 expected,compare_exchange_weak 会把当前值写入 expected // 并返回 false,循环继续尝试 }

这段代码实现了无锁的自增操作。即使多个线程同时执行,也能保证最终结果正确。

简单的无锁栈实现

下面是一个基于链表的无锁栈示例,展示如何使用原子指针和 CAS 构建线程安全的数据结构:

template
struct lock_free_stack {
    struct node {
        T data;
        node* next;
        node(T const& d) : data(d), next(nullptr) {}
    };
std::atomiczuojiankuohaophpcnnode*youjiankuohaophpcn head{nullptr};

void push(T const& data) {
    node* new_node = new node(data);
    new_node-youjiankuohaophpcnnext = head.load();
    while (!head.compare_exchange_weak(new_node-youjiankuohaophpcnnext, new_node)) {
        // 如果 head 被其他线程修改,new_node-youjiankuohaophpcnnext 会被更新为最新值
        // 继续尝试直到成功
    }
}

T pop() {
    node* old_head = head.load();
    while (old_head && !head.compare_exchange_weak(old_head, old_head-youjiankuohaophpcnnext)) {
        // 尝试将 head 指向下一个节点
    }
    if (old_head) {
        T result = old_head-youjiankuohaophpcndata;
        delete old_head;
        return result;
    }
    throw std::runtime_error("empty stack");
}

};

注意:这个简单实现存在 ABA 问题和内存回收风险,在生产环境中需结合 hazard pointer 或 epoch-based 回收机制。

基本上就这些。合理使用 std::atomic 能有效减少锁竞争,提高并发性能,但需要仔细处理内存序和异常情况。无锁编程虽高效,但也更复杂,建议在真正需要性能优化时再采用。