天天看點

自旋鎖和互斥鎖差別

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq_34173549/article/details/81057190

POSIX threads(簡稱Pthreads)是在多核平台上進行并行程式設計的一套常用的API。線程同步(Thread Synchronization)是并行程式設計中非常重要的通訊手段,其中最典型的應用就是用Pthreads提供的鎖機制(lock)來對多個線程之間共 享的臨界區(Critical Section)進行保護(另一種常用的同步機制是barrier)。

Pthreads提供了多種鎖機制:

(1) Mutex(互斥量):pthread_mutex_***

(2) Spin lock(自旋鎖):pthread_spin_***

(3) Condition Variable(條件變量):pthread_con_***

(4) Read/Write lock(讀寫鎖):pthread_rwlock_***

Pthreads提供的Mutex鎖操作相關的API主要有:

pthread_mutex_lock (pthread_mutex_t *mutex);

pthread_mutex_trylock (pthread_mutex_t *mutex);

pthread_mutex_unlock (pthread_mutex_t *mutex);

Pthreads提供的與Spin Lock鎖操作相關的API主要有:

pthread_spin_lock (pthread_spinlock_t *lock);

pthread_spin_trylock (pthread_spinlock_t *lock);

pthread_spin_unlock (pthread_spinlock_t *lock);

從 實作原理上來講,Mutex屬于sleep-waiting類型的鎖。例如在一個雙核的機器上有兩個線程(線程A和線程B),它們分别運作在Core0和 Core1上。假設線程A想要通過pthread_mutex_lock操作去得到一個臨界區的鎖,而此時這個鎖正被線程B所持有,那麼線程A就會被阻塞 (blocking),Core0 會在此時進行上下文切換(Context Switch)将線程A置于等待隊列中,此時Core0就可以運作其他的任務(例如另一個線程C)而不必進行忙等待。而Spin lock則不然,它屬于busy-waiting類型的鎖,如果線程A是使用pthread_spin_lock操作去請求鎖,那麼線程A就會一直在 Core0上進行忙等待并不停的進行鎖請求,直到得到這個鎖為止。

是以,自旋鎖一般用用多核的伺服器。 

自旋鎖(Spin lock)

自旋鎖與互斥鎖有點類似,隻是自旋鎖不會引起調用者睡眠,如果自旋鎖已經被别的執行單元保持,調用者就一直循環在那裡看是 否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是是以而得名。其作用是為了解決某項資源的互斥使用。因為自旋鎖不會引起調用者睡眠,是以自旋鎖的效率遠 高于互斥鎖。雖然它的效率比互斥鎖高,但是它也有些不足之處:

    1、自旋鎖一直占用CPU,他在未獲得鎖的情況下,一直運作--自旋,是以占用着CPU,如果不能在很短的時 間内獲得鎖,這無疑會使CPU效率降低。

    2、在用自旋鎖時有可能造成死鎖,當遞歸調用時有可能造成死鎖,調用有些其他函數也可能造成死鎖,如 copy_to_user()、copy_from_user()、kmalloc()等。

是以我們要慎重使用自旋鎖,自旋鎖隻有在核心可搶占式或SMP的情況下才真正需要,在單CPU且不可搶占式的核心下,自旋鎖的操作為空操作。自旋鎖适用于鎖使用者保持鎖時間比較短的情況下。

       自旋鎖的用法如下:

   首先定義:spinlock_t x;

 然後初始化:spin_lock_init(spinlock_t *x);   //自旋鎖在真正使用前必須先初始化

 在2.6.11核心中将定義和初始化合并為一個宏:DEFINE_SPINLOCK(x)

  獲得自旋鎖:spin_lock(x);   //隻有在獲得鎖的情況下才傳回,否則一直“自旋”

                           spin_trylock(x);  //如立即獲得鎖則傳回真,否則立即傳回假

      釋放鎖:spin_unlock(x);

結合以上有以下代碼段:

    spinlock_t lock;        //定義一個自旋鎖

    spin_lock_init(&lock);

    spin_lock(&lock);    

    .......        //臨界區

    spin_unlock(&lock);   //釋放鎖

    還有一些其他用法:

spin_is_locked(x)

    //  該宏用于判斷自旋鎖x是否已經被某執行單元保持(即被鎖),如果是,   傳回真,否則傳回假。

spin_unlock_wait(x)

    //  該宏用于等待自旋鎖x變得沒有被任何執行單元保持,如果沒有任何執行單元保持該自旋鎖,該宏立即傳回,否

    //将循環    在那裡,直到該自旋鎖被保持者釋放。

spin_lock_irqsave(lock, flags)

    //  該宏獲得自旋鎖的同時把标志寄存器的值儲存到變量flags中并失效本地中//斷。相當于:spin_lock()+local_irq_save()

spin_unlock_irqrestore(lock, flags)

    //  該宏釋放自旋鎖lock的同時,也恢複标志寄存器的值為變量flags儲存的//值。它與spin_lock_irqsave配對使用。

    //相當于:spin_unlock()+local_irq_restore()

spin_lock_irq(lock)

     //該宏類似于spin_lock_irqsave,隻是該宏不儲存标志寄存器的值。相當         //于:spin_lock()+local_irq_disable()

spin_unlock_irq(lock)

    //該宏釋放自旋鎖lock的同時,也使能本地中斷。它與spin_lock_irq配對應用。相當于: spin_unlock()+local_irq+enable()

spin_lock_bh(lock)

    //  該宏在得到自旋鎖的同時失效本地軟中斷。相當于:  //spin_lock()+local_bh_disable()

spin_unlock_bh(lock)

      //該宏釋放自旋鎖lock的同時,也使能本地的軟中斷。它與spin_lock_bh配對//使用。相當于:spin_unlock()+local_bh_enable()

spin_trylock_irqsave(lock, flags)

    //該宏如果獲得自旋鎖lock,它也将儲存标志寄存器的值到變量flags中,并且失//效本地中斷,如果沒有獲得鎖,它什麼也不做。是以如果能夠立即 獲得鎖,它等//同于spin_lock_irqsave,如果不能獲得鎖,它等同于spin_trylock。如果該宏//獲得自旋鎖lock,那需要 使用spin_unlock_irqrestore來釋放。

spin_trylock_irq(lock)

    //該宏類似于spin_trylock_irqsave,隻是該宏不儲存标志寄存器。如果該宏獲得自旋鎖lock,需要使用spin_unlock_irq來釋放。

spin_trylock_bh(lock)

    //  該宏如果獲得了自旋鎖,它也将失效本地軟中斷。如果得不到鎖,它什麼//也不做。是以,如果得到了鎖,它等同于spin_lock_bh,如果得 不到鎖,它等同//于spin_trylock。如果該宏得到了自旋鎖,需要使用spin_unlock_bh來釋放。

spin_can_lock(lock)

    //  該宏用于判斷自旋鎖lock是否能夠被鎖,它實際是spin_is_locked取反。//如果lock沒有被鎖,它傳回真,否則,傳回 假。該宏在2.6.11中第一次被定義,在//先前的核心中并沒有該宏。

繼續閱讀