原子操做,線程間交互數據最細粒度的同步操做,它能夠保證線程間讀寫某個數值的原子性。macos
因爲不須要加劇量級的互斥鎖進行同步,所以很是輕量,並且也不須要在內核間來回切換調度,效率是很是高的。。windows
那如何使用原子操做了,各個平臺下都有相關api提供了支持,而且向gcc、clang這些編譯器,也提供了編譯器級的__builtin接口進行支持api
__sync_val_compare_and_swap
和__sync_val_compare_and_swap_8
等__builtin接口lock
彙編指令先拿tbox的tb_atomic_fetch_and_add
接口爲例,顧名思義,這個api會先讀取原有數值,而後在其基礎上加上一個數值:多線程
// 至關於原子進行:b = *a++; tb_atomic_t a = 0; tb_long_t b = tb_atomic_fetch_and_add(&a, 1);
若是須要先進行add計算,再返回結果能夠用:架構
// 至關於原子進行:b = ++*a; tb_atomic_t a = 0; tb_long_t b = tb_atomic_add_and_fetch(&a, 1);
或者能夠更加簡化爲:函數
tb_long_t b = tb_atomic_fetch_and_inc(&a); tb_long_t b = tb_atomic_inc_and_fetch(&a);
那tbox在內部如何去適配各個平臺的呢,咱們能夠簡單看下,基本上就是對原生api進行了一層wrap而已。fetch
static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_windows(tb_atomic_t* a, tb_long_t v) { return (tb_long_t)InterlockedExchangeAdd((LONG __tb_volatile__*)a, v); } static __tb_inline__ tb_long_t tb_atomic_inc_and_fetch_windows(tb_atomic_t* a) { return (tb_long_t)InterlockedIncrement((LONG __tb_volatile__*)a); }
static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_sync(tb_atomic_t* a, tb_long_t v) { return __sync_fetch_and_add(a, v); }
static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_x86(tb_atomic_t* a, tb_long_t v) { /* * xaddl v, [a]: * * o = [a] * [a] += v; * v = o; * * cf, ef, of, sf, zf, pf... maybe changed */ __tb_asm__ __tb_volatile__ ( #if TB_CPU_BITSIZE == 64 "lock xaddq %0, %1 \n" //!< xaddq v, [a] #else "lock xaddl %0, %1 \n" //!< xaddl v, [a] #endif : "+r" (v) : "m" (*a) : "cc", "memory" ); return v; }
原子操做除了能夠進行對int32和int64數值加減乘除外,還能夠進行xor, or, and等邏輯計算,用法相似,這裏就很少說了。ui
下面咱們再來個簡單的實例,來實際運用下,原子的應用場景仍是蠻多的,好比:atom
等等。。線程
咱們先來看下如何去實現一個簡單的自旋鎖,爲了統一規範演示代碼,下面的代碼都用tbox提供的原子接口爲例:
static __tb_inline_force__ tb_bool_t tb_spinlock_init(tb_spinlock_ref_t lock) { // init *lock = 0; // ok return tb_true; } static __tb_inline_force__ tb_void_t tb_spinlock_exit(tb_spinlock_ref_t lock) { // exit *lock = 0; } static __tb_inline_force__ tb_void_t tb_spinlock_enter(tb_spinlock_ref_t lock) { /* 嘗試讀取lock的狀態值,若是還沒獲取到lock(狀態0),則獲取它(設置爲1) * 若是對方線程已經獲取到lock(狀態1),那麼循環等待嘗試從新獲取 * * 注:整個狀態讀取和設置,是原子的,沒法被打斷 */ tb_size_t tryn = 5; while (tb_atomic_fetch_and_pset((tb_atomic_t*)lock, 0, 1)) { // 沒獲取到lock,嘗試5次後,還不成功,則讓出cpu切到其餘線程運行,以後從新嘗試獲取 if (!tryn--) { // yield tb_sched_yield(); // reset tryn tryn = 5; } } } static __tb_inline_force__ tb_void_t tb_spinlock_leave(tb_spinlock_ref_t lock) { // 釋放lock,此處無需原子,設置到一半被打斷,數值部位0,對方線程仍是在等待中,不收影響 *((tb_atomic_t*)lock) = 0; }
這個實現很是簡單,可是tbox裏面,基本上默認都是在使用這個spinlock,由於tbox裏面大部分多線程實現,粒度都被拆的很細
大部分狀況下,用自旋鎖就ok了,無需進入內核態切換等待。。
使用方式以下:
// 獲取lock tb_spinlock_enter(&lock); // 一些同步操做 // .. // 釋放lock tb_spinlock_leave(&lock);
上面的代碼中,省略了init和exit操做,實際使用時,在響應初始化和釋放的地方,作相應處理下就好了。。
pthread_once
的實現pthread_once
能夠在多線程函數內,能夠保證傳入的函數只被調用到一次,通常能夠用來初始化全局單例或者TLS的key初始化
以tbox的接口爲例,我先來來看下,這個函數的使用方式:
// 初始化函數,只會被調用到一次 static tb_void_t tb_once_func(tb_cpointer_t priv) { // 初始化一些單例對象,全局變量 // 或者執行一些初始化調用 } // 線程函數 static tb_int_t tb_thread_func(tb_cpointer_t priv) { // 全局存儲lock,並初始化爲0 static tb_atomic_t lock = 0; if (tb_thread_once(&lock, tb_once_func, "user data")) { // ok } }
咱們這裏拿原子操做,能夠簡單模擬實現下這個函數:
tb_bool_t tb_thread_once(tb_atomic_t* lock, tb_bool_t (*func)(tb_cpointer_t), tb_cpointer_t priv) { // check tb_check_return_val(lock && func, tb_false); /* 原子獲取lock的狀態 * * 0: func尚未被調用 * 1: 已經獲取到lock,func正在被其餘線程調用中 * 2: func已經被調用完成,而且func返回ok * -2: func已經被調用,而且func返回失敗failed */ tb_atomic_t called = tb_atomic_fetch_and_pset(lock, 0, 1); // func已經被其餘線程調用過了?直接返回 if (called && called != 1) { return called == 2; } // func尚未被調用過?那麼調用它 else if (!called) { // 調用函數 tb_bool_t ok = func(priv); // 設置返回狀態 tb_atomic_set(lock, ok? 2 : -1); // ok? return ok; } // 正在被其餘線程獲取到lock,func正在被調用中,還沒完成?嘗試等待lock else { // 此處簡單的作了些sleep循環等待,直到對方線程func執行完成 tb_size_t tryn = 50; while ((1 == tb_atomic_get(lock)) && tryn--) { // wait some time tb_msleep(100); } } /* 從新獲取lock的狀態,判斷是否成功 * * 成功:2 * 超時:1 * 失敗:-2 * * 此處只要不是2,都算失敗 */ return tb_atomic_get(lock) == 2; }
64位操做跟32位的接口使用方式,是徹底同樣的,僅僅只是變量類型的區別:
tb_atomic64_t
,接口改成tb_atomic64_xxxx
volatile long long
,接口改成__sync_xxxx_8
系列具體使用方式參考32位,這裏就不詳細介紹了。。
我的主頁:TBOOX開源工程