MySQL Server層的原子操作
阿新 • • 發佈:2020-02-04
看如下程式碼
#test.cc int i=0; void* funcA(void* arg) { for(int i=0;i<10;i++) { ++i; } } void* funcB(void* arg) { for(inti=0;i<10;i--) { --i; } } int main() { pthread_create(..funcAdd..); pthread_create(..funcAdd..); cout << i << endl; return 0; }
多執行緒併發更新全域性變數i的值,最終的結果是不確定的。
要想得到預期結果0,需要在對變數i進行遞增或者遞減操作時,加鎖處理。
程式碼如下:
#test.cc pthread_mutex_t m_i; int i=0; void* funcA(void* arg) { for(int i=0;i<10;i++) { lock(&m_i); ++i; unlock(&m_i); } } void* funcB(void* arg) { for(inti=0;i<10;i--) { lock(&m_i); --i; unlock(&m_i); } } int main() { pthread_create(..funcAdd..); pthread_create(..funcAdd..); cout << i << endl; }
但是加鎖等待,被喚醒,解鎖這一系列操作,意味著需要進行頻繁的上下文切換,影響效能。為了避免這個問題,gcc4.1.2版本之後,提供了一系列的原子操作,也就說,不需要引入鎖的保護,來實現對變數的併發操作。
MySQL對這些操作進行了封裝,InnoDB內部spin lock的實現也是利用了原子操作,server層也提供了同樣的原子操作。
來看一個MySQL server層的示例:Global_THD_manager中表示當前處於running狀態的執行緒數的成員變數,如下:
volatile int32 num_thread_running;
每當有一個執行緒從sleep狀態轉為執行狀態時,都需要讓num_thread_running遞增,而結束執行時,需要遞減,這就和本文開始的場景完全一樣。如果通過鎖來保護num_thread_running變數,在高併發下必定會影響效能。
負責遞增和遞減num_thread_running變數的兩個函式
void inc_thread_running()
{
my_atomic_add32(&num_thread_running,1);
}
/**
Decrements thread running statistic variable.
*/
void dec_thread_running()
{
my_atomic_add32(&num_thread_running,-1);
}
其中my_atomic_add32的實現如下:
static inline int32 my_atomic_add32(int32 volatile *a,int32 v)
{
return __sync_fetch_and_add(a,v);
}
它通過__sync_fetch_and_add函式來實現對變數a的add操作。
下圖是MySQL提供的其他原子操作集合
別的模組不直接使用過__sync_fetch_and_add等一系列的函式,而是使用封裝過後的以my*打頭的函式。