1. 程式人生 > >多執行緒和多核下“鎖”的應用

多執行緒和多核下“鎖”的應用

 

假設這樣一種情況:有多個執行緒(或多核)需要在共享資料A滿足某一條件時,對A進行操作.

以下舉例兩種實現

Fun_1()

{ lock()--------------1.1

  Result=Check(A)-----1.2

  Unlock()------------1.3

                     -------------RISK!!!!

  Lock()--------------1.4

  Operate(A,result)---1.5

  Unlock()------------1.6

}

Fun_2()

{ lock()--------------2.1

  Result=Check(A)-----2.2

  Operate(A,result)---2.3

  Unlock()------------2.4

}

以上函式,只有fun_2 是安全的,FUN_1存在風險:

單核下:

當前執行緒完1.3   後如果發生了執行緒排程,那麼資料a可能被其它執行緒修改。

重新回到1.4時,RESULT 已經不對了!!!

多核下:

當前執行緒完1.3   後如果發生了執行緒排程,那麼資料a可能被其它執行緒修改。

重新回到1.4時,RESULT 已經不對了!!!

當前執行緒完1.3   後如果第二個核正好在執行程式碼1.5,那麼資料a會被修改。

重新回到1.4時,RESULT 已經不對了!!!

***********************************************************************************************

鎖的選擇分為“訊號量”“和自旋轉鎖”:

訊號量會使程式休眠

自旋鎖會關搶佔,並進行忙等待

一般如果訪問時間較短,或者使用多核處理器可以選擇自旋鎖

如果訪問的時間較長(大於系統進行兩次排程需要的時間)且是單核處理器,則可以選擇訊號量。

************************************************************************************************

linux下自旋鎖函式:

spin_lock                                       使用情況:如果被保護的共享資源“只在程序上下文訪問”    

                                                        功能:        最基本的鎖,沒有對中斷處理

spin_lock_bh                                適用情況:如果被保護的共享資源“只在程序上下文和tasklet或timer上下文訪問”

                                                        功能:        spin_lock為基礎,釋放時開啟上鎖時禁止的軟中斷

spin_lock_irq                                適用情況:如果被保護的共享資源“只在程序上下文和tasklet或timer上下文訪問”

                                                        功能:        spin_lock為基礎,釋放時開啟中斷

spin_lock_irqsave                       適用情況:效率最差的一種,但當你不知道用哪個好時,可以用它

                                                        功能:        spin_lock為基礎,釋放時恢復上鎖時的中斷狀態

*******************************************************************************************************************

以下說明摘自網上:

如果被保護的共享資源“只在程序上下文訪問和軟中斷上下文訪問”,那麼當在程序上下文訪問共享資源時,可能被軟中斷打斷,從而可能進入軟中斷上下文來對被保護的共享資源訪問,因此對於這種情況,對共享資源的訪問必須使用spin_lock_bh和spin_unlock_bh來保護。

  當然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它們失效了本地硬中斷,失效硬中斷隱式地也失效了軟中斷。但是使用spin_lock_bh和spin_unlock_bh是最恰當的,它比其他兩個快。   如果被保護的共享資源“只在程序上下文和tasklet或timer上下文訪問”,那麼應該使用與上面情況相同的獲得和釋放鎖的巨集,因為tasklet和timer是用軟中斷實現的。   如果被保護的共享資源“只在一個tasklet或timer上下文訪問”,那麼不需要任何自旋鎖保護,因為同一個tasklet或timer只能在一個CPU上執行,即使是在SMP環境下也是如此。實際上tasklet在呼叫tasklet_schedule標記其需要被排程時已經把該tasklet繫結到當前CPU,因此同一個tasklet決不可能同時在其他CPU上執行。   timer也是在其被使用add_timer新增到timer佇列中時已經被幫定到當前CPU,所以同一個timer絕不可能執行在其他CPU上。當然同一個tasklet有兩個例項同時執行在同一個CPU就更不可能了。   如果被保護的共享資源“只在兩個或多個tasklet或timer上下文”訪問,那麼對共享資源的訪問僅需要用spin_lock和spin_unlock來保護,不必使用_bh版本,因為當tasklet或timer執行時,不可能有其他tasklet或timer在當前CPU上執行。  如果被保護的共享資源“只在一個軟中斷(tasklet和timer除外)上下文訪問”,那麼這個共享資源需要用spin_lock和spin_unlock來保護,因為同樣的軟中斷可以同時在不同的CPU上執行。   如果被保護的共享資源在兩個或多個軟中斷上下文訪問,那麼這個共享資源當然更需要用spin_lock和spin_unlock來保護,不同的軟中斷能夠同時在不同的CPU上執行。   如果被保護的共享資源在軟中斷(包括tasklet和timer)或程序上下文和硬中斷上下文訪問,那麼在軟中斷或程序上下文訪問期間,可能被硬中斷打斷,從而進入硬中斷上下文對共享資源進行訪問,因此,在程序或軟中斷上下文需要使用spin_lock_irq和spin_unlock_irq來保護對共享資源的訪問。   而在中斷處理控制代碼中使用什麼版本,需依情況而定,如果只有一箇中斷處理控制代碼訪問該共享資源,那麼在中斷處理控制代碼中僅需要spin_lock和spin_unlock來保護對共享資源的訪問就可以了。   因為在執行中斷處理控制代碼期間,不可能被同一CPU上的軟中斷或程序打斷。但是如果有不同的中斷處理控制代碼訪問該共享資源,那麼需要在中斷處理控制代碼中使用spin_lock_irq和spin_unlock_irq來保護對共享資源的訪問。   在使用spin_lock_irq和spin_unlock_irq的情況下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具體應該使用哪一個也需要依情況而定,如果可以確信在對共享資源訪問前中斷是使能的,那麼使用spin_lock_irq更好一些。   因為它比spin_lock_irqsave要快一些,但是如果你不能確定是否中斷使能,那麼使用spin_lock_irqsave和spin_unlock_irqrestore更好,因為它將恢復訪問共享資源前的中斷標誌而不是直接使能中斷。   當然,有些情況下需要在訪問共享資源時必須中斷失效,而訪問完後必須中斷使能,這樣的情形使用spin_lock_irq和spin_unlock_irq最好。