muduo網路庫學習筆記(2):原子性操作

li27z發表於2016-08-04

所謂原子操作是指不會被執行緒排程機制打斷的操作,這種操作一旦開始,就一直執行到結束,中間不會有任何 context switch (切換到另一個執行緒)。在多程式(執行緒)訪問資源時,能夠確保所有其他的程式(執行緒)都不在同一時間內訪問相同的資源。

C/C++ 中數值操作,如自加 (n++) 自減 (n- -) 及賦值 (n=2) 操作都不是原子操作。如果多執行緒程式需要使用全域性計數器,程式就需要使用鎖或者互斥量保證操作的安全性,對於較高併發的程式,這種做法會造成一定的效能瓶頸。

muduo網路庫對原子性操作的封裝有如下要點:

(1)gcc提供的原子操作

// 原子自增操作,返回的是更新前的值
type __sync_fetch_and_add (type *ptr, type value)

// 原子比較和交換(設定)操作
// 如果*ptr == oldval,就將newval寫入*ptr,第一個函式返回操作之前的值,第二個函式在相等並寫入的情況下返回true
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval)
bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval)

// 原子賦值操作,將*ptr設為value並返回*ptr操作之前的值
type __sync_lock_test_and_set (type *ptr, type value)

使用這些原子性操作,編譯的時候需要加-march=cpu-type(如native,i386,pentium等等)

muduo原子性操作的程式碼中,就是利用gcc提供的原子操作來實現賦值、自加等原子性操作。

檔名:Atomic.h

  T get()
  {
    return __sync_val_compare_and_swap(&value_, 0, 0);
  }

  // value++
  T getAndAdd(T x)
  {
    return __sync_fetch_and_add(&value_, x);
  }

  T addAndGet(T x)
  {
    return getAndAdd(x) + x;
  }

(2)volatile關鍵字
volatile的作用: 作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。簡單地說就是防止編譯器對程式碼進行優化。當要求使用volatile 宣告的變數的值的時候,系統總是重新從它所在的記憶體讀取資料,而不是使用儲存在暫存器中的備份。即使它前面的指令剛剛從該處讀取過資料。而且讀取的資料立刻被儲存。

(3)explicit關鍵字與隱式轉換
C++中的explicit關鍵字只能用於修飾只有一個引數的類建構函式,它的作用是表明該建構函式是顯示的, 而非隱式的,跟它相對應的另一個關鍵字是implicit, 意思是隱藏的,類建構函式預設情況下即宣告為implicit(隱式)。隱式轉換總是在我們沒有察覺的情況下悄悄發生,除非有心所為,隱式轉換常常是我們所不希望發生的。通過將建構函式宣告為explicit(顯式)的方式可以抑制隱式轉換。也就是說,explicit建構函式必須顯式呼叫。

通過一個例子來理解。

class Test1
{
public:
  Test1(int n) { num = n; }  // 普通建構函式
private:
  int num;
};

class Test2
{
public:
  explicit Test2(int n) { num = n; }  // explicit(顯式)建構函式
private:
  int num;
};

int main()
{
  Test1 t1 = 12;  // 成功,隱式呼叫其建構函式Test1(int n),再呼叫預設的拷貝建構函式 
  Test2 t2 = 12;  // 編譯錯誤,不能隱式呼叫其建構函式
  Test2 t3(12);   // 成功,顯式呼叫其建構函式explicit Test2(int n);
  return 0;
}

相關文章