前面我們講了C++11下的多線程及相關操作,這些操作在絕大多數情況下應該夠用了。但在某些極端場合,如需要高性能的情況下,我們還需要一些更高效的同步手段。本節介紹的原子操作是一種lock free的操作,不需要同步鎖,具有很高的性能。在化學中原子不是可分割的最小單位,引申到編程中,原子操作是不可打斷的最低粒度操作,是線程安全的。C++11中原子類提供的成員函數都是原子的,是線程安全的。
原子操作中最簡單的莫過于atomic_flag,只有兩種操作:test and set、clear。我們的原子操作就從這種類型開始。
1. std::atomic_flag
C++11中所有的原子類都是不允許拷貝、不允許Move的,atomic_flag也不例外。atomic_flag顧名思議,提供了標志的管理,標志有三種狀態:clear、set和未初始化狀態。
1.1 atomic_flag實例化
缺省情況下atomic_flag處于未初始化狀態。除非初始化時使用了ATOMIC_FLAG_INIT
宏,則此時atomic_flag處于clear狀態。
1.2 std::atomic_flag::clear
調用該函數將會把atomic_flag置為clear狀態。clear狀態您可以理解為bool類型的false,set狀態可理解為true狀態。clear函數沒有任何返回值:
void clear(memory_order m = memory_order_seq_cst) volatile noexcept;
void clear(memory_order m = memory_order_seq_cst) noexcept;
對于memory_order我們會在后面的章節中詳細介紹它,現在先列出其取值及簡單釋義
序號 | 值 | 意義 |
---|---|---|
1 | memory_order_relaxed | 寬松模型,不對執行順序做保證 |
2 | memory_order_consume | 當前線程中,滿足happens-before原則。 當前線程中該原子的所有后續操作,必須在本條操作完成之后執行 |
3 | memory_order_acquire | 當前線程中,讀操作滿足happens-before原則。 所有后續的讀操作必須在本操作完成后執行 |
4 | memory_order_release | 當前線程中,寫操作滿足happens-before原則。 所有后續的寫操作必須在本操作完成后執行 |
5 | memory_order_acq_rel | 當前線程中,同時滿足memory_order_acquire和memory_order_release |
6 | memory_order_seq_cst | 最強約束。全部讀寫都按順序執行 |
1.3 test_and_set
該函數會檢測flag是否處于set狀態,如果不是,則將其設置為set狀態,并返回false;否則返回true。
test_and_set是典型的read-modify-write(RMW)模型,保證多線程環境下只被設置一次。下面代碼通過10個線程,模擬了一個計數程序,第一個完成計數的會打印"win"。
#include <atomic> // atomic_flag
#include <iostream> // std::cout, std::endl
#include <list> // std::list
#include <thread> // std::thread
void race(std::atomic_flag &af, int id, int n) {
for (int i = 0; i < n; i++) {
}
// 第一個完成計數的打印:Win
if (!af.test_and_set()) {
printf("%s[%d] win!!!\n", __FUNCTION__, id);
}
}
int main() {
std::atomic_flag af = ATOMIC_FLAG_INIT;
std::list<std::thread> lstThread;
for (int i = 0; i < 10; i++) {
lstThread.emplace_back(race, std::ref(af), i + 1, 5000 * 10000);
}
for (std::thread &thr : lstThread) {
thr.join();
}
return 0;
}
程序輸出如下(每次運行,可能率先完成的thread不同):
race[7] win!!!
上一篇 C++11多線程-線程局部存儲 |
目錄 | 下一篇 C++11多線程-原子操作(2) |
---|