多核編程中的線程分組競(jìng)爭(zhēng)模式
在多核編程中,鎖競(jìng)爭(zhēng)導(dǎo)致的CPU饑餓現(xiàn)象是引起多核CPU性能無(wú)法發(fā)揮的最重要原因之一,在多核編程中的鎖競(jìng)爭(zhēng)難題一文中已經(jīng)講過(guò)鎖競(jìng)爭(zhēng)對(duì)性能的影響,如何消解鎖競(jìng)爭(zhēng)導(dǎo)致的CPU饑餓現(xiàn)象成了迫切需要解決的問(wèn)題。
目 前業(yè)界發(fā)展的無(wú)鎖編程技術(shù)可以有效降低鎖競(jìng)爭(zhēng)引起的性能下降問(wèn)題,無(wú)鎖編程主要是采用原子操作來(lái)替代鎖,只存在原子操作競(jìng)爭(zhēng)問(wèn)題,由于原子操作只是一條指 令,速度非??欤虼丝梢越频乜闯墒菬o(wú)鎖競(jìng)爭(zhēng)的,除非原子操作非常頻繁。無(wú)鎖編程難度非常高,從目前的情況來(lái)看,普通程序員要親自進(jìn)行無(wú)鎖編程是不現(xiàn)實(shí) 的事情。并且目前只有少數(shù)數(shù)據(jù)結(jié)構(gòu)可以實(shí)現(xiàn)無(wú)鎖編程,從目前商用的無(wú)鎖編程庫(kù)NOBLE來(lái)看,只提供了隊(duì)列、棧、鏈表、詞典、帶引用計(jì)數(shù)的垃圾回收內(nèi)存管理等少數(shù)幾種無(wú)鎖編程結(jié)構(gòu),只能解決部分鎖競(jìng)爭(zhēng)問(wèn)題,這個(gè)庫(kù)的售價(jià)高昂,以下是從NOBLE的網(wǎng)站上拷貝下來(lái)的售價(jià)。
$1395 USD, NOBLE Professional Edition, Evaluation License 1 Months, Windows
$3295 USD, NOBLE Professional Edition, Evaluation License 3 Months, Windows
看了這個(gè)售價(jià),估計(jì)國(guó)內(nèi)也沒(méi)有多少公司愿意出這么高昂的價(jià)錢的購(gòu)買這個(gè)一個(gè)功能有限,且使用起來(lái)不像以前的使用鎖的庫(kù)那么方便的庫(kù)。
既然沒(méi)有無(wú)鎖編程的免費(fèi)午餐可以享用,那么使用鎖來(lái)編程的話,可不可以避免鎖競(jìng)爭(zhēng)導(dǎo)致的CPU饑餓現(xiàn)象呢?答案是可以的,這就是文章標(biāo)題中的線程分組競(jìng)爭(zhēng)模式,它使用鎖來(lái)進(jìn)行保護(hù)共享數(shù)據(jù),但是又避免了鎖競(jìng)爭(zhēng)時(shí)出現(xiàn)CPU饑餓現(xiàn)象。其實(shí)這個(gè)模式在業(yè)界已經(jīng)有了很成功的實(shí)踐,那就是隊(duì)列池。當(dāng)然這個(gè)模式的應(yīng)用不僅僅限于隊(duì)列池,也可以用到很多其他的滿足一定條件的共享數(shù)據(jù)保護(hù)上。
先看一下線程分組競(jìng)爭(zhēng)模式的基本思想,所謂線程分組競(jìng)爭(zhēng)就是將線程分成若干個(gè)組,每個(gè)組的線程間存在鎖競(jìng)爭(zhēng),但是不同組之間的線程不存在鎖競(jìng)爭(zhēng)。下圖以添加操作和刪除操作作為示例來(lái)顯示2個(gè)分組線程競(jìng)爭(zhēng)的情況:

圖中顯示了兩個(gè)分組的線程競(jìng)爭(zhēng)情況,共有四個(gè)線程分成兩組進(jìn)行競(jìng)爭(zhēng),添加操作線程1和刪除操作線程1之間存在鎖競(jìng)爭(zhēng)情況,添加操作線程2和刪除操作線程2之間存在鎖競(jìng)爭(zhēng)情況,但是添加操作線程1(或刪除操作線程1)和添加操作線程2和刪除操作線程2之間不存在鎖競(jìng)爭(zhēng)。
在這種分組鎖競(jìng)爭(zhēng)模式下,任意一組線程中,至少有一個(gè)線程在執(zhí)行,因此如果有N組線程的話,那么至少有N個(gè)線程在執(zhí)行,如果N大于等于CPU的核數(shù),那么任意一個(gè)CPU 核上都有線程一直在運(yùn)行,可以充分保證CPU不會(huì)產(chǎn)生饑餓現(xiàn)象。
并不是任意共享數(shù)據(jù)都可以采用線程分組競(jìng)爭(zhēng)的形式來(lái)進(jìn)行訪問(wèn),共享數(shù)據(jù)必須可以分成若干個(gè)獨(dú)立的子數(shù)據(jù),每次操作只需要操作某個(gè)子數(shù)據(jù),一次操作中不需要操作多個(gè)子數(shù)據(jù)。
線程分組競(jìng)爭(zhēng)模式和無(wú)鎖編程相比,有著很大的優(yōu)勢(shì),最重要的優(yōu)勢(shì)有兩點(diǎn):
一、使用有鎖編程,編程難度很小,易于為普通程序員掌握。
二、并發(fā)性比無(wú)鎖編程更好,無(wú)鎖編程中存在原子操作競(jìng)爭(zhēng)問(wèn)題,其競(jìng)爭(zhēng)激烈程度會(huì)隨CPU核數(shù)增加而增加,特別是當(dāng)原子操作包含在大循環(huán)中時(shí),原子操作競(jìng)爭(zhēng)最壞情況下會(huì)使性能降到和單核CPU一樣。而線程分組模式中各個(gè)CPU核完全是并行運(yùn)行的,CPU核相互間不存在競(jìng)爭(zhēng)問(wèn)題,因此CPU核數(shù)增加不會(huì)造成任何影響。
以上講的是線程分組競(jìng)爭(zhēng)模式的一種情況,實(shí)際情況中很多情況和這種模式并不完全符合,因此線程分組模式存在一些變種以適應(yīng)更多的實(shí)際情況。下圖便是一個(gè)最常見(jiàn)的變種:

如上圖所示,在這個(gè)變種中添加操作線程只有一個(gè),而刪除操作線程卻有兩個(gè),添加操作線程A如果操作子內(nèi)存區(qū)域1,則使用lock1,操作子內(nèi)存區(qū)域2時(shí)則使用lock2。添加操作線程A可以和刪除操作線程1和刪除操作線程2存在鎖競(jìng)爭(zhēng),但是刪除操作線程1和刪除操作線程2之間不存在鎖競(jìng)爭(zhēng)。從運(yùn)行情況來(lái)看,3個(gè)線程中至少有2個(gè)在同時(shí)運(yùn)行,如果有N個(gè)子內(nèi)存區(qū)域和N個(gè)刪除操作線程的話,那么至少有N個(gè)線程在同時(shí)運(yùn)行,因此這種鎖競(jìng)爭(zhēng)模式中,同樣可以保證當(dāng)CPU核數(shù)少于等于線程分組的個(gè)數(shù)時(shí),不會(huì)發(fā)生CPU饑餓現(xiàn)象。隊(duì)列池便是這種模式的一個(gè)很好的成功實(shí)踐。
線程分組競(jìng)爭(zhēng)模式是消除鎖競(jìng)爭(zhēng)造成多核CPU性能下降的最有效方式,它的性能近似于單核CPU中的多線程編程的性能,這種模式也是設(shè)計(jì)本地分布式數(shù)據(jù)結(jié)構(gòu)的一種有效方法。























