在 C++ 中,标准库提供了一些原子类型,主要用于多线程编程,以确保对变量的操作是原子的。以下是一些常见的原子类型:
std::atomic<bool>
std::atomic<char>
std::atomic<signed char>
std::atomic<unsigned char>
std::atomic<short>
std::atomic<unsigned short>
std::atomic<int>
std::atomic<unsigned int>
std::atomic<long>
std::atomic<unsigned long>
std::atomic<long long>
std::atomic<unsigned long long>
std::atomic<wchar_t>
std::atomic<char16_t>
std::atomic<char32_t>
std::atomic<std::intptr_t>
std::atomic<std::uintptr_t>
std::atomic<std::size_t>
std::atomic<std::ptrdiff_t>
std::atomic<void*>
std::atomic<std::nullptr_t>
此外,std::atomic
还可以用于用户定义的类型,但这些类型必须是 trivially copyable 的。
例如:
#include <atomic>
#include <string>std::atomic<int> atomicInt{0};
std::atomic<bool> atomicBool{false};
std::atomic<std::string*> atomicStringPtr{nullptr};
这些类型可以确保在多线程环境中进行安全的读写操作。
std::atomic<void*> 代表的是指针类型吗
是的,std::atomic<void*>
代表的是一个原子指针类型。它可以用于存储和操作指针,并确保这些操作在多线程环境中是原子的。
例如:
#include <atomic>int main() {int a = 42;std::atomic<void*> atomicPtr{&a};// 读取指针值void* ptrValue = atomicPtr.load();// 修改指针值int b = 24;atomicPtr.store(&b);return 0;
}
在这个例子中,atomicPtr
是一个原子指针,可以安全地在多个线程之间共享和修改。
C++有那些相关的原子操作的api函数呢
在 C++ 中,标准库提供了一些与原子操作相关的 API 函数,这些函数主要在 <atomic>
头文件中定义。以下是一些常见的原子操作 API 函数:
-
加载和存储操作
load()
store()
-
交换操作
exchange()
-
比较并交换操作
compare_exchange_weak()
compare_exchange_strong()
-
算术操作
fetch_add()
fetch_sub()
fetch_and()
fetch_or()
fetch_xor()
-
内存顺序
memory_order_relaxed
memory_order_consume
memory_order_acquire
memory_order_release
memory_order_acq_rel
memory_order_seq_cst
以下是一些示例代码,展示了如何使用这些 API 函数:
#include <atomic>
#include <iostream>int main() {std::atomic<int> atomicInt{0};// 加载和存储操作atomicInt.store(10);int value = atomicInt.load();std::cout << "Value: " << value << std::endl;// 交换操作int oldValue = atomicInt.exchange(20);std::cout << "Old Value: " << oldValue << std::endl;std::cout << "New Value: " << atomicInt.load() << std::endl;// 比较并交换操作int expected = 20;bool exchanged = atomicInt.compare_exchange_strong(expected, 30);std::cout << "Exchanged: " << exchanged << std::endl;std::cout << "Current Value: " << atomicInt.load() << std::endl;// 算术操作atomicInt.fetch_add(5);std::cout << "After fetch_add: " << atomicInt.load() << std::endl;atomicInt.fetch_sub(3);std::cout << "After fetch_sub: " << atomicInt.load() << std::endl;return 0;
}
这些 API 函数可以确保在多线程环境中进行安全的原子操作。
compare_exchange_weak()
是 C++ 中的一个原子操作函数,用于在多线程环境中进行无锁编程。它尝试将原子对象的值与预期值进行比较,如果相等,则将其替换为新值。与 compare_exchange_strong()
不同,compare_exchange_weak()
可能会因为伪失败而返回 false,因此通常用于循环中。
以下是 compare_exchange_weak()
的用法示例:
#include <atomic>
#include <iostream>int main() {std::atomic<int> atomicInt{10};int expected = 10;int desired = 20;// compare_exchange_weak() 可能会伪失败,因此通常在循环中使用while (!atomicInt.compare_exchange_weak(expected, desired)) {// 如果失败,expected 会被更新为 atomicInt 的当前值std::cout << "Compare and exchange failed, expected: " << expected << std::endl;}std::cout << "Compare and exchange succeeded, new value: " << atomicInt.load() << std::endl;return 0;
}
在这个示例中,compare_exchange_weak()
尝试将 atomicInt
的值从 expected
(10)更改为 desired
(20)。如果 atomicInt
的当前值与 expected
相等,则更改成功并返回 true;否则,返回 false 并更新 expected
为 atomicInt
的当前值。由于 compare_exchange_weak()
可能会伪失败,因此通常在循环中使用它,直到操作成功为止。
什么是 “伪失败” ?
在 C++ 中,compare_exchange_weak()
和 compare_exchange_strong()
都是用于原子比较并交换操作的函数。它们的主要区别在于 compare_exchange_weak()
可能会出现“伪失败”(spurious failure)。
伪失败(Spurious Failure)
伪失败是指在没有其他线程干扰的情况下,compare_exchange_weak()
也可能返回 false。这种情况通常是由于硬件实现的原因,特别是在某些架构上,弱的比较并交换操作可能会偶尔失败,即使预期值和当前值相等。
为什么使用 compare_exchange_weak()
?
尽管 compare_exchange_weak()
可能会出现伪失败,但它通常比 compare_exchange_strong()
更高效,因为它允许硬件使用更轻量级的指令。由于伪失败的可能性,compare_exchange_weak()
通常在循环中使用,以确保操作最终成功。
示例代码
以下是一个使用 compare_exchange_weak()
的示例,展示了如何处理伪失败:
#include <atomic>
#include <iostream>int main() {std::atomic<int> atomicInt{10};int expected = 10;int desired = 20;// compare_exchange_weak() 可能会伪失败,因此通常在循环中使用while (!atomicInt.compare_exchange_weak(expected, desired)) {// 如果失败,expected 会被更新为 atomicInt 的当前值std::cout << "Compare and exchange failed, expected: " << expected << std::endl;}std::cout << "Compare and exchange succeeded, new value: " << atomicInt.load() << std::endl;return 0;
}
在这个示例中,compare_exchange_weak()
尝试将 atomicInt
的值从 expected
(10)更改为 desired
(20)。如果 atomicInt
的当前值与 expected
相等,则更改成功并返回 true;否则,返回 false 并更新 expected
为 atomicInt
的当前值。由于 compare_exchange_weak()
可能会伪失败,因此通常在循环中使用它,直到操作成功为止。
实现无锁队列
实现无锁队列(Lock-Free Queue)是一项复杂的任务,通常需要使用高级的原子操作和内存序列。以下是一个简单的无锁队列实现示例,使用了 C++11 的原子操作。
无锁队列实现示例
#include <atomic>
#include <memory>
#include <iostream>template<typename T>
class LockFreeQueue {
private:struct Node {std::shared_ptr<T> data;std::atomic<Node*> next;Node(T value) : data(std::make_shared<T>(value)), next(nullptr) {}};std::atomic<Node*> head;std::atomic<Node*> tail;public:LockFreeQueue() {Node* dummy = new Node(T());head.store(dummy);tail.store(dummy);}~LockFreeQueue() {while (Node* old_head = head.load()) {head.store(old_head->next);delete old_head;}}void enqueue(T value) {Node* new_node = new Node(value);Node* old_tail = tail.load();while (true) {Node* next = old_tail->next.load();if (next == nullptr) {if (old_tail->next.compare_exchange_weak(next, new_node)) {tail.compare_exchange_weak(old_tail, new_node);return;}} else {tail.compare_exchange_weak(old_tail, next);}old_tail = tail.load();}}std::shared_ptr<T> dequeue() {Node* old_head = head.load();while (true) {Node* next = old_head->next.load();if (next == nullptr) {return std::shared_ptr<T>(); // Queue is empty}if (head.compare_exchange_weak(old_head, next)) {std::shared_ptr<T> result = next->data;delete old_head;return result;}old_head = head.load();}}
};int main() {LockFreeQueue<int> queue;queue.enqueue(1);queue.enqueue(2);queue.enqueue(3);std::shared_ptr<int> value;while ((value = queue.dequeue())) {std::cout << *value << std::endl;}return 0;
}
代码解释
- Node 结构体:每个节点包含一个数据指针和一个指向下一个节点的原子指针。
- 构造函数和析构函数:初始化队列时创建一个哑节点,并在析构时清理所有节点。
- enqueue 方法:将新节点添加到队列尾部。使用
compare_exchange_weak
确保操作是原子的。 - dequeue 方法:从队列头部移除节点。使用
compare_exchange_weak
确保操作是原子的。
这个实现是一个简单的无锁队列示例,适用于基本的多线程环境。对于更复杂的需求,可能需要更高级的无锁数据结构和算法。