歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux多線程同步方式

當多個線程共享相同的內存時,需要確保每個線程看到一致的數據視圖,當多個線程同時去修改這片內存時,就可能出現偏差,得到與預期不符合的值。為啥需要同步,一件事情邏輯上一定是有序的,即使在並發環境下;而操作系統對於多線程不會自動幫我們串行化,所以需要我們通過操作系統提供的同步方式api,結合自己的業務邏輯,利用多線程提高性能的同時,保證業務邏輯的正確性。一般而言,linux下同步方式主要有4種,原子鎖,互斥量,讀寫鎖和條件變量。下面一一介紹幾種同步方式。

1. spinlock

1)  概念

spinlock是一種互斥結構,通過CPU提供的特殊的原子指令集合實現互斥地訪問一個資源,需要硬件支持。一個線程訪問數據未結束的時候,其他線程不得對同一個數據進行訪問。

2)  實現

spinlock一般基於原子的read-modify-write操作實現。read-modify-write操作允許一個CPU讀取一個值,修改該值,並將修改完成的值回寫內存的三個操作作為一個原子總線操作,因此需要CPU特殊支持。具體而言,通過test-and-set指令實現,從內存中讀取一個值,然後和0比較,並且將內存中的值設置為1。

3) 相關函數

  a) 原子操作

1  test_and_set(volatile int* addr, value)
2  {         
3        return os_atomic_test_and_set_int(addr,value);
4    }

  這裡volatile修飾詞告訴編譯器從內存中獲取,保證正確性,避免從寄存器中讀取到不准確的值。

  b) 設置鎖變量


 1 set_spinlock(lock_word)
 2 {
 3          int i = 0;
 4          int value;
 5          while (true)
 6          {
 7              value = test_and_set(&lock_word, 1);
 8              if (value == 0) //尚未被占用,可以獲取
 9                      break;
10              else  //已經被其他線程占用,繼續輪轉
11                    do nothing
12          }
13 }

c)      重置鎖變量

1 reset_spinlock(lock_word)
2 {
3    test_and_set (lock_word, 0);
4 }

2.  mutex

  1) 概念

  與 spinlock作用相同,保證互斥地訪問一個資源。

  2) 相關函數

  int pthread_mutex_lock(pthread_mutex_t *mutex)

  int pthread_mutex_trylock(pthread_mutex_t *mutex)

  int pthread_mutex_unlock(pthread_mutex_t *mutex)

  pthread_mutex_trylock()語義與pthread_mutex_lock()類似,不同的是在鎖已經被占據時返回EBUSY而不是掛起等待。

  3)  spinlock與mutex的區別

      a) 作用范圍,mutex是內核對象,可以在多線程,多進程同步中使用;spinlock作用范圍僅限於本進程(鎖變量是進程內的),僅適用於多線程同步。

      b) spinlock依賴於硬件的原子操作指令

      c) 線程獲取spinlock失敗時,會采取循環等待的方式,此時線程處於運行狀態,CPU空轉;而獲取mutex失敗時,線程會掛起,線程處於wait狀態,不會被內核調度。

    d) 由於3的特點,進入等待狀態或從等待狀態被喚醒,都涉及到CPU的上下文切換,而CPU切換是比較耗時的,一般需要25us。相對而言spinlock則沒有這樣的代價,效率更高。

    e) 也由於3的特點,spinlock會空轉,導致浪費大量的CPU時間片,若用戶持有鎖時間長,導致空轉時間長,也得不償失。因此spinlock比較適合於“快拿快放”的使用場景。

3.讀寫鎖

1) 概念

spinlock和互斥量都是保證同一時刻只有一個線程操作共享內存。互斥鎖要麼是加鎖狀態,要麼是不加鎖狀態,一次只有一個線程可以對其加鎖。讀寫所可以有3種狀態,讀模式下加鎖狀態,寫模式下加鎖狀態,不加鎖狀態。相對於前兩者,讀寫鎖有更高的並發度,允許多個線程同時讀共享內存。

2) 相關函數

pthread_rwlock_rdlock(pthread_rwlock_t*);  讀鎖定

pthread_rwlock_tryrdlock(pthread_rwlock_t*); 非阻塞讀鎖定

pthread_rwlock_wrlock(pthread_rwlock_t*); 寫鎖定

pthread_rwlock_trywrlock(pthread_rwlock_t*); 非阻塞寫鎖定

pthread_rwlock_unlock(pthread_rwlock_t*);  釋放鎖

4. 條件變量

1) 概念

條件變量是另外一種同步機制,通過與互斥鎖配合使用,利用鎖保護條件變量,通過條件變量實現喚醒和等待的機制。通過這種方式,允許線程以無競爭的方式等待特定的條件發生

2) 相關函數

int pthread_cond_signal(pthread_cond_t *cond);  //喚醒等待條件某個線程

int pthread_cond_broadcast(pthread_cond_t *cond); //喚醒等待條件所有線程

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); //等待條件發生。

3) 說明

調用pthread_cond_wait之前,需要線程獲取互斥量,調用者把互斥量傳遞給函數,函數把調用線程發到等待隊列上,然後對互斥量解鎖,這個操作是原子操作。當pthread_cond_wait返回時,互斥量會再次被鎖住,這個實現都在pthread_cond_wait函數中實現,不需要用戶邏輯介入。

Copyright © Linux教程網 All Rights Reserved