C++11中引入了原子变量模板类型,原子变量的引入,极大的方便了开发者开发多并发程序。工作中也经常使用原子变量,今天针对具体类型std::atomic<unsigned long long>和自定义类型std::atomic<Node*>使用做个小小的总结。
std::atomic<unsigned long long>原子变量的常用操作有:
(1) -- ++操作
(2)store(写操作)
(3)load(读操作)
(4)exchange(交换数据)
(5)compare_exchange_weak (CAS)
(6)compare_exchange_strong (加强版的CAS,性能略差)
说明:
compare_exchange_weak和compare_exchange_strong都是比较并更新操作,功能类似,compare_exchange_weak允许偶然出乎意料的返回false,在原始值和比较值一致时,通常比compare_exchange_strong有更高的性能。函数会传入比较值和更新值,进行如下操作:
1)如果原始值与比较值一致,则将原始值修改为更新值,并返回true
2)如果原始值与比较值不一致,则将比较值修改为原始值,并返回false
1.简单累加操作
在多线程中通常需要统计某个操作调用了多少次,此时可以利用原子变量统计比加锁更加高效和快捷,举例如下:
#include <atomic>
#include <thread>
static std::atomic<unsigned long long> s_sequence_index(0);
void no_lock_statistics() {
std::thread th1([&]{
for (int i = 0; i < 1000000; i++) {
++s_sequence_index;
}
});
std::thread th2([&]{
for (int i = 0; i < 1000000; i++) {
++s_sequence_index;
}
});
std::thread th3([&]{
for (int i = 0; i < 1000000; i++) {
++s_sequence_index;
}
});
std::thread th4([&]{
for (int i = 0; i < 1000000; i++) {
++s_sequence_index;
}
});
th1.join();
th2.join();
th3.join();
th4.join();
std::cout << "statistics:: " << s_sequence_index.load() << std::endl;
}
int main() {
no_lock_statistics();
return 0;
}
运行结果如下:
通过运行看,4个线程同时对一个原子变量操作1000000次++,操作结束原子变量变为4000000,如果s_sequence_index改为普通的类型,操作结束结果不会变为4000000。
2.获取并更新值操作
在实际多线程开发中,往往需要比较,获取和更新某个变量,这一系列操作想要保证原子操作,如果没有原子变量,往往需要增加互斥锁。有了原子变量之后,操作就会变得很容易
#include <atomic>
#include <thread>
static std::atomic<unsigned long long> s_sequence_index(0);
uint64_t get_index() {
unsigned long long _index = 0;
do {
_index = s_sequence_index;
} while(!s_sequence_index.compare_exchange_weak(_index, _index + 1));
return (uint64_t)_index;
}
void test_get_index() {
for (int i = 0; i < 100; i++) {
std::thread([&]{
std::cout << get_index() << std::endl;
}).detach();
}
getchar();
std::cout << "end:: " << s_sequence_index << std::endl;
}
int main() {
test_get_index();
return 0;
}
运行结果如下:
如上图示,多线程不重复的获取原子变量的值,并对其进行更新,保证原子变量值的连续性和排他性。
自定义类型原子变量std::atomic<Node*>的常用操作有:
(1)store(写操作)
(2)load(读操作)
(3)exchange(交换数据)
(4)compare_exchange_weak (CAS)
(5)compare_exchange_strong
如下将展示使用原子操作多线程操作链表的例子
#include <atomic>
#include <thread>
template<typename T>
class LockFreeList
{
private:
struct Node
{
T _data;
Node* _next;
Node(T const& data): _data(data), _next(nullptr) {
}
};
public:
LockFreeList() : _head(nullptr) {
}
void push(T const& data) {
Node* new_node = new Node(data);
new_node->_next = _head.load();
while (!_head.compare_exchange_weak(new_node->_next, new_node));
}
void show() {
Node* tmpNode = _head;
while(tmpNode) {
std::cout << tmpNode->_data << " ";
tmpNode = tmpNode->_next;
}
std::cout << std::endl;
}
private:
std::atomic<Node*> _head;
};
static LockFreeList<int> _s_lock_free_list;
static std::atomic<int> _s_free_int(0);
void test_lock_free_list() {
for (int i = 0; i < 1000; i++) {
std::thread([&](){
_s_lock_free_list.push(_s_free_int++);
}).detach();
}
getchar();
_s_lock_free_list.show();
}
int main() {
test_lock_free_list();
return 0;
}
运行结果如下:
通过测试以看到,数据被无重复的插入到了链表之中。