C++ 语言 原子操作的无锁编程

C++阿木 发布于 2025-06-14 8 次阅读


C++ 无锁编程:原子操作与无锁算法

在多线程编程中,确保数据的一致性和线程安全是至关重要的。无锁编程(Lock-Free Programming)提供了一种避免使用锁来同步线程的方法,从而提高程序的性能和可伸缩性。C++11及以后的版本提供了原子操作库,使得无锁编程变得更加容易实现。本文将围绕C++语言中的原子操作,探讨无锁编程的基本概念、实现方法以及一些常见的无锁算法。

原子操作

原子操作是指不可分割的操作,即在任何时刻,该操作要么完全执行,要么完全不执行。在多线程环境中,原子操作可以保证数据的一致性和线程安全。

C++11 原子操作库

C++11 引入了 `` 头文件,其中定义了一系列原子类型和原子操作。以下是一些常用的原子类型和操作:

- `std::atomic`:表示一个原子类型的变量。
- `std::atomic_load()`:原子地加载一个变量的值。
- `std::atomic_store()`:原子地存储一个变量的值。
- `std::atomic_exchange()`:原子地交换两个变量的值。
- `std::atomic_compare_exchange_weak()`:原子地比较并交换变量的值。

示例

以下是一个简单的示例,演示如何使用 `std::atomic` 来实现线程安全的计数器:

cpp
include
include
include

std::atomic counter(0);

void increment() {
for (int i = 0; i < 100000; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}

int main() {
const int num_threads = 10;
std::thread threads[num_threads];

for (int i = 0; i < num_threads; ++i) {
threads[i] = std::thread(increment);
}

for (auto& t : threads) {
t.join();
}

std::cout << "Final counter value: " << counter.load(std::memory_order_relaxed) << std::endl;

return 0;
}

在这个例子中,我们创建了一个 `std::atomic` 类型的变量 `counter`,并在多个线程中对其进行了增加操作。由于 `counter` 是原子的,因此我们不需要额外的同步机制。

无锁算法

无锁算法是利用原子操作来实现线程安全的数据结构和算法。以下是一些常见的无锁算法:

无锁队列

无锁队列是一种线程安全的队列,它允许并发地插入和删除元素。以下是一个简单的无锁队列实现:

cpp
include
include
include

template
class LockFreeQueue {
private:
struct Node {
std::shared_ptr data;
std::atomic next;
};

std::atomic head;
std::atomic tail;

public:
LockFreeQueue() {
Node sentry = new Node();
sentry->next = sentry;
head = sentry;
tail = sentry;
}

void push(const T& value) {
std::shared_ptr new_node(new Node());
new_node->data = std::make_shared(value);
new_node->next = head.load(std::memory_order_relaxed);

while (!tail.compare_exchange_weak(head.load(std::memory_order_acquire), new_node, std::memory_order_release, std::memory_order_acquire)) {
// Spin-wait
}
}

bool pop(T& value) {
Node h = head.load(std::memory_order_acquire);
Node t = tail.load(std::memory_order_acquire);

if (h == t) {
return false;
}

Node next = h->next.load(std::memory_order_acquire);
value = next->data;
if (tail.compare_exchange_weak(t, next, std::memory_order_release, std::memory_order_acquire)) {
return true;
}

return false;
}
};

在这个实现中,我们使用了一个循环链表来存储队列元素,并通过原子操作来保证线程安全。

无锁栈

无锁栈是一种线程安全的栈,它允许并发地插入和删除元素。以下是一个简单的无锁栈实现:

cpp
include
include

template
class LockFreeStack {
private:
struct Node {
std::shared_ptr data;
std::atomic next;
};

std::atomic head;

public:
LockFreeStack() {
head = nullptr;
}

void push(const T& value) {
std::shared_ptr new_node(new Node());
new_node->data = std::make_shared(value);
new_node->next = head.load(std::memory_order_relaxed);

while (!head.compare_exchange_weak(new_node, new_node->next, std::memory_order_release, std::memory_order_relaxed)) {
// Spin-wait
}
}

bool pop(T& value) {
Node h = head.load(std::memory_order_acquire);
if (h == nullptr) {
return false;
}

value = h->data;
if (head.compare_exchange_weak(h, h->next, std::memory_order_release, std::memory_order_relaxed)) {
return true;
}

return false;
}
};

在这个实现中,我们使用了一个循环链表来存储栈元素,并通过原子操作来保证线程安全。

总结

无锁编程是一种提高程序性能和可伸缩性的有效方法。C++11 原子操作库为无锁编程提供了强大的支持。通过使用原子操作和无锁算法,我们可以实现线程安全的并发数据结构和算法。无锁编程也具有一定的挑战性,需要仔细设计以确保线程安全性和性能。本文介绍了C++中的原子操作和无锁算法,为读者提供了无锁编程的基础知识。