C++11:原子操作

在多線程開發中,爲了確保數據安全性,經常需要對數據進行加鎖、解鎖處理。C++11中引入了原子的概念,簡而言之就是訪問它時它自動加鎖解鎖,從而使軟件開發更爲簡便。

原子可謂一個既簡單又複雜的概念。簡單到訪問它時就跟單線程訪問一塊內存一樣簡單,複雜的地方在於它的實現涉及到各種內存模型,在優化中經常會遇到。

下面給出一個簡單的原子示例:

#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic_int val = { 0 };//這個類型也可以寫作 atomic<int> 用於表示整型數據的原子

void icrement () {
	for (int i = 0; i < 100000000; i++) {
		val++;
	}
}

int main (int argc, char* argv []) {
	//創建兩個線程
	thread t1 (icrement);
	thread t2 (icrement);
	//等待兩個線程執行完
	t1.join ();
	t2.join ();
	cout << val << endl;
	return 0;
}
經過十幾秒左右的等待後,代碼執行完畢,結果不出所料,200000000。簡單的原子操作差不多就是這樣,atomic模板可以包括任何類型,另外原子的操作也與它本身的操作方式基本相同,因爲原子模板重載了所有的運算符。
簡單的說完了,說說複雜的原子概念。

假如一個原子,它長這樣

atomic_int val = { 0 };
嗯,跟上面的相同。它實際上可以提供三種類型的操作:讀、寫、RMW(同時包括讀寫),通過三種類型的函數實現。
首先是讀,比如 int i=val; 這樣的代碼,實際上是通過load函數實現。

int i = val.load (memory_order_seq_cst);
後面的參數代表內存順序。這個的含義是順序執行當前的原子操作。什麼含義?含義就是,如果一個函數中對這個原子進行了多項操作,那麼首先執行之前的原子操作,然後執行本條操作,最後執行之後的原子操作。說白了就是單線程的執行順序。原子的操作過程並不是必須固定的,一個函數中如果有兩條原子操作,那麼首先執行後面操作,然後執行前面操作是完全可能的。這個在優化中經常會遇到。
然後是寫操作,比如 val = i; 這樣的操作

val.store (i, memory_order_seq_cst);
嗯,這兒也順序執行,以免顛覆各位三觀。

然後就是同時讀寫這樣的操作了。 比如原子+=一個數之後同時可訪問,通過compare_exchange這類函數實現。
然後,接下來說說內存訪問模型了。一共有六種

1、memory_order_seq_cst    順序執行,可用於讀、寫、RMW操作

2、memory_order_relaxed    亂序執行,可用於讀、寫、RMW操作

3、memory_order_acq_rel    首先執行之前的寫操作,然後執行本條操作,然後執行之後的讀操作,可用於RMW操作

4、memory_order_release    首先執行之前的寫操作,然後執行本條操作,可用於寫、RMW操作

5、memory_order_acquire    首先執行本條操作,然後執行後面的讀操作,可用於讀、RMW操作

6、memory_order_consume    首先執行本條操作,然後執行後面的讀寫操作,可用於讀、RMW操作

基本的概念就是上面這些了,接下來動手實踐吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章