在許多為了效能和效率的場景下,需要開發一些lock-free的演算法和資料結構
atomic_flag
原子布林型別,只支援 test-and-set 和 clear 操作
建構函式
atomic_flag()noexcept=default;
atomic_flag(const atomic_flag&)=delete;
只有預設建構函式,而不能從其他物件構造
atomic_flag
物件需要使用ATOMIC_FLAG_INIT
初始化,使其處於 clear 狀態,否則是unspecified未指定的
建立10個執行緒用於計數,先完成計數任務的執行緒輸出id
#include<iostream>
#include<thread>
#include<atomic>
#include<vector>
std::atomic<bool> ready(false);
std::atomic_flag winner=ATOMIC_FLAG_INIT;
void count1m(int id){
while(!ready){
std::this_thread::yield();
}
for(int i=0; i<10000; ++i){
// count
}
// 執行完後,該函式
if(!winner.test_and_set()){
std::cout<<"thread "<<id<<" first\n";
}
};
int main(){
std::vector<std::thread> ths;
std::cout<<"spawning 10 threads that count to 1 million..."<<std::endl;
for(int i=1; i<=10; ++i){
ths.push_back(std::thread(count1m, i));
}
ready= true;
for(auto& th:ths){
th.join();
}
return 0;
}
std::atomic_flag::test_and_set
函式原型如下:
bool test_and_set(memory_order sync=memory_order_seq_cst)volatile noexcept;
bool test_and_set(memory_order sync=memory_order_seq_cst)noexcept;
用於檢查標誌,若std::atomic_flag
之前被設定過,則返回true
,否則返回false
並設定標誌
該操作為原子操作,可以指定的Memory Order如下
值 | 型別 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
#include<iostream>
#include<atomic>
#include<thread>
#include<vector>
#include<sstream>
std::atomic_flag lock_stream=ATOMIC_FLAG_INIT;
std::stringstream stream;
void append_number(int x){
while(lock_stream.test_and_set()){}
stream<<"thread "<<x<<'\n';
lock_stream.clear();
}
int main(){
std::vector<std::thread> ths;
for(int i=1; i<=10; ++i){
ths.push_back(std::thread(append_number,i));
}
for(auto& th:ths){
th.join();
}
std::cout<<stream.str()<<std::endl;
return 0;
}
std::atomic_flag::clear
清除物件標誌,即設定為false
函式原型如下:
void clear(memory_order sync=memory_order_seq_cst)volatile noexcept;
void clear(memory_order sync=memory_order_seq_cst)noexcept;
該函式也可以指定Memory Order的值
std::atomic_flag
可以當作一個簡單的自旋鎖使用
#include<iostream>
#include<thread>
#include<vector>
#include<atomic>
std::atomic_flag lock= ATOMIC_FLAG_INIT;
void f(int n){
for(int cnt=0; cnt<100; ++i){
while(lock.test_and_set(std::memory_order_acquire))
; //spin
std::cout<<"output from thread "<<n<<'\n';
lock.clear(std::memory_order_release);
}
}
int main() {
std::vector<std::thread> ths;
for(int n=0; n<10; ++n){
ths.emplace_back(f,n);
}
for(auto& th:ths){
th.join();
}
return 0;
}
對std::atomic_flag
的加鎖操作可以理解為lock.test_and_set(std::memory_order_acquire);
加鎖操作時,返回false
表示加鎖成功
因為此前lock
的標誌為false
,呼叫test_and_set()
後標誌為true
,說明某一執行緒獲得了鎖
對std::atomic_flag
的解鎖操作相當於lock.test_and_set(std::memory_order_release);
atomic
原子型別atomic_flag
過於簡單,下面介紹功能更完善的std::atomic
原子型別物件的特點:不同執行緒訪問不會導致資料競爭data race問題
建構函式
std::atomic
是一個模板類
template<class T>
struct atomic;
template<>
struct atomic<integral>{};
template<class T>
struct atomic<T*>{};
標準庫提供了對整型和指標型別的特化實現
預設建構函式
atomic()noexcept=default;
物件處於未初始化狀態,需要使用atomic_init
進行初始化
初始化建構函式
constexpr atomic(T val)noexcept;
可用T
物件對atomic
進行初始化
複製建構函式
atomic(const atomic&)=delete;
被禁用,不可複製
#include<iostream>
#include<atomic>
#include<thread>
#include<vector>
std::atomic<bool> ready(false);
std::atomic_flag winner= ATOMIC_FLAG_INIT;
void do_count1m(int id){
while(!ready){
std::this_thread::yield();
}
for(volatile int i=0; i<1000000; ++i){
}
if(!winner.test_and_set()){
std::cout<<"thread "<<id<<" first\n";
}
}
int main(){
std::vector<std::thread> ths;
std::cout<<"spawning 10 threads that count to 1 million...\n";
for(int i=1; i<=10; ++i){
ths.push_back(std::thread(count1m,i));
}
ready= true;
for(auto& th:ths){
th.join();
}
return 0;
}
成員函式
賦值操作
std::atomic::operator=()
函式
copy (deleted)
atomic& operator=(const atomic&)=delete;
atomic& operator=(const atomic&)volatile=delete;
賦值運算是被禁用的
set value
T operator=(T val)noexcept;
T operator=(T val)volatile noexcept;
過載了賦值運算子,使得原子型別可以被型別為T
的變數賦值,類似於隱式轉換
該操作是原子操作,記憶體序預設為順序一致性std::memory_order_seq_cst
若需要使用其他記憶體序,可以使用std::atomic::store()
#include<iostream>
#include<atomic>
#include<thread>
std::atomic<int> foo=0;
void set_foo(int x){
foo=x;
}
void print_foo(){
while(foo==0){
std::this_thread::yield();
}
std::cout<<"foo:"<<foo<<std::endl;
}
int mian(){
std::thread first(print_foo);
std::thread second(set_foo, 10);
first.join();
second.join();
return 0;
}
常用成員函式
bool is_lock_free()const volatile noexcept;
bool is_lock_free()const noexcept;
判斷物件是否有lock-free特性
若物件具有該特性,則多執行緒訪問該物件時不會導致執行緒阻塞
void store(T val, memory_order sync=memory_order_seq_cst)volatile noexcept;
void store(T val, memory_order sync=memory_order_seq_cst)noexcept;
修改被封裝的值,將val
複製給原子物件所封裝的值,sync
指定記憶體序
值 | 型別 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
示例如下
#include<iostream>
#include<atomic>
#include<thread>
std::atomic<int> foo(0);
void set_foo(int x){
foo.store(x, std::memory_order_relaxed);
}
void print_foo(){
int x;
do{
x= foo.load(std::memory_order_relaxed);
}while(x==0);
std::cout<<"foo:"<<x<<std::endl;
}
int main(){
std::thread first(print_foo);
std::thread second(set_foo, 10);
first.join();
second.join();
return 0;
}
T load(memory_order sync=memory_order_seq_cst)const volatile noexcept;
T load(memory_order sync=memory_order_seq_cst)const noexcept;
讀取被封裝的值,引數sync
設定記憶體序
值 | 型別 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memory_order_seq_cst | Sequentially consistent |
示例如下
#include<iostream>
#include<atomic>
#include<thread>
std::atomic<int> foo(0);
void set_foo(int x){
foo.store(x, std::memory_order_relaxed);
}
void print_foo(){
int x;
do{
x= foo.load(std::memory_order_relaxed);
}while(x==0);
std::cout<<"foo:"<<x<<'\n';
}
int main(){
std::thread first(print_foo);
std::thread second(set_foo, 10);
first.join();
second.join();
return 0;
}
operator T()const volatile noexcept;
operator T()const noexcept;
與load
功能類似,讀取封裝的值,預設記憶體序是std::memory_order_seq_cst
示例如下
T exchange(T val, memory_order sync=memory_order_seq_cst)volatile noexcept;
T exchange(T val, memory_order sync=memory_order_seq_cst)noexcept;
compare_exchange_weak
compare_exchange_strong
1
std::atomic
對整型和指標型別特化
成員函式
fetch_add
fetch_sub
fetch_and
fetch_or
fetch_xor
operator++
operator--
2
C++11原子操作中C風格的API
std::atomic和std::atomic_flag