相比多進(jìn)程模型,多線程模型最大的優(yōu)勢在于數(shù)據(jù)共享非常方便,同一進(jìn)程內(nèi)的多個線程可以使用相同的地址值訪問同一塊內(nèi)存數(shù)據(jù)。但是,當(dāng)多個線程對同一塊內(nèi)存數(shù)據(jù)執(zhí)行“讀?處理?更新”操作時,會由于線程的交叉執(zhí)行而造成數(shù)據(jù)的錯誤。 例如以下代碼段,當(dāng) thread_func() 同時在多個線程中執(zhí)行時,更新到 glob_value 中的值就會互相干擾,產(chǎn)生錯誤結(jié)果。
解決這類問題的關(guān)鍵在于,當(dāng)一個線程正在執(zhí)行“讀?處理?更新”操作時,保證其他線程不會中途闖入與其交叉執(zhí)行。不可被打斷的執(zhí)行序列稱為臨界區(qū),保證多個線程不會交叉執(zhí)行同一臨界區(qū)的技術(shù)稱為線程同步。 1 互斥鎖的使用最常用的線程同步技術(shù)是互斥鎖,Linux 線程庫中的相關(guān)函數(shù)有:
這里pthread的p代表POSIX線程 所有線程都有一個線程號,也就是Thread ID。其類型為pthread_t。通過調(diào)用pthread_self()函數(shù)可以獲得自身的線程號。 pthread_mutex_lock() 負(fù)責(zé)在進(jìn)入臨界區(qū)之前對臨界區(qū)加鎖; 當(dāng)某個線程試圖給一個已經(jīng)處在加鎖狀態(tài)的臨界區(qū)再次加鎖時,該線程就會被臨時掛起,一直等到該臨界區(qū)被解鎖后,才會被喚醒并繼續(xù)執(zhí)行。 如果同時有多個線程等待某個臨界區(qū)解鎖,那下次被喚醒的進(jìn)程取決于內(nèi)核的調(diào)度策略,并沒有固定的順序。 靜態(tài)分配的 mutex 變量在使用之前應(yīng)該被初始化為 PTHREAD_MUTEX_INITIALIZER,而動態(tài)分配的 mutex 需要調(diào)用 pthread_mutex_init() 進(jìn)行初始化,且只被某個線程初始化一次,可以利用 pthread_once() 函數(shù)方便完成。
多個線程在臨界區(qū)上的執(zhí)行是串行的,開發(fā)者應(yīng)該盡量減少程序在臨界區(qū)內(nèi)的停留時間,以提高程序的并行性。因此,臨界區(qū)不應(yīng)該包含任何非必須的邏輯,以及任何可能帶來高延遲的 IO 等操作 2 互斥鎖的保護(hù)范圍和使用順序對互斥鎖加鎖的不恰當(dāng)使用會造成線程的死鎖,比如下面這兩種情況。
因此,開發(fā)者需要仔細(xì)規(guī)劃互斥鎖保護(hù)范圍和使用順序 3 避免死鎖的兩個加鎖函數(shù)為了避免出現(xiàn)死鎖問題,可以使用另外兩種變體的鎖定函數(shù),如下所示:
前者可以在鎖定失敗后立即返回,后者可以在一段超時時間后返回, 應(yīng)用這兩個函數(shù)可以處理這種錯誤情況,而避免陷入無限的死鎖中。 在 Linux 中,實(shí)現(xiàn)互斥鎖采用的是 Futex(Fast Userspace Mutex)方案。在該實(shí)現(xiàn)中,只有發(fā)生了鎖的爭用才需要陷入到內(nèi)核空間中處理,否則所有的操作都可以在用戶空間內(nèi)快速完成。在大多數(shù)情況下,互斥鎖本身的效率很高,其平均開銷大約相當(dāng)于幾十次內(nèi)存讀寫和算數(shù)運(yùn)算所花費(fèi)的時間。 來源:http://www./content-4-220351.html |
|