http://blog.csdn.net/kyokowl/article/details/6294341html
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上進行忙等待並不停的進行鎖請求,直到獲得這個鎖爲止。linux
因此,自旋鎖通常用用多核的服務器。 編程
自旋鎖(Spin lock)服務器
自旋鎖與互斥鎖有點相似,只是自旋鎖不會引發調用者睡眠,若是自旋鎖已經被別的執行單元保持,調用者就一直循環在那裏看是 否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是所以而得名。其做用是爲了解決某項資源的互斥使用。由於自旋鎖不會引發調用者睡眠,因此自旋鎖的效率遠 高於互斥鎖。雖然它的效率比互斥鎖高,可是它也有些不足之處:
一、自旋鎖一直佔用CPU,他在未得到鎖的狀況下,一直運行--自旋,因此佔用着CPU,若是不能在很短的時 間內得到鎖,這無疑會使CPU效率下降。
二、在用自旋鎖時有可能形成死鎖,當遞歸調用時有可能形成死鎖,調用有些其餘函數也可能形成死鎖,如 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中第一次被定義,在//先前的內核中並無該宏。app
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////less
兩種鎖的加鎖原理ide
互斥鎖:線程會從sleep(加鎖)——>running(解鎖),過程當中有上下文的切換,cpu的搶佔,信號的發送等開銷。函數
自旋鎖:線程一直是running(加鎖——>解鎖),死循環檢測鎖的標誌位,機制不復雜。post
兩種鎖的區別url
互斥鎖的起始原始開銷要高於自旋鎖,可是基本是一勞永逸,臨界區持鎖時間的大小並不會對互斥鎖的開銷形成影響,而自旋鎖是死循環檢測,加鎖全程消耗cpu,起始開銷雖然低於互斥鎖,可是隨着持鎖時間,加鎖的開銷是線性增加。
兩種鎖的應用
互斥鎖用於臨界區持鎖時間比較長的操做,好比下面這些狀況均可以考慮
1 臨界區有IO操做
2 臨界區代碼複雜或者循環量大
3 臨界區競爭很是激烈
4 單核處理器
至於自旋鎖就主要用在臨界區持鎖時間很是短且CPU資源不緊張的狀況下。
自旋-互斥鎖
下面的英文介紹了混合互斥鎖和混合自旋鎖,可是不論是第一段說的先上非阻塞鎖後上阻塞鎖,仍是第二段說的先自旋上鎖後進行休眠,反正思路都是先自旋上鎖必定時間後在上互斥鎖,這種自旋-互斥鎖適合各線程持鎖時間間隔跨度比較大的狀況。
A hybrid mutex behaves like a spinlock at first on a multi-core system. If a thread cannot lock the mutex, it won't be put to sleep immediately, since the mutex might get unlocked pretty soon, so instead the mutex will first behave exactly like a spinlock. Only if the lock has still not been obtained after a certain amount of time (or retries or any other measuring factor), the thread is really put to sleep. If the same system runs on a system with only a single core, the mutex will not spinlock, though, as, see above, that would not be beneficial.
A hybrid spinlock behaves like a normal spinlock at first, but to avoid wasting too much CPU time, it may have a back-off strategy. It will usually not put the thread to sleep (since you don't want that to happen when using a spinlock), but it may decide to stop the thread (either immediately or after a certain amount of time) and allow another thread to run, thus increasing chances that the spinlock is unlocked (a pure thread switch is usually less expensive than one that involves putting a thread to sleep and waking it up again later on, though not by far).