(1)鎖使用實(shí)踐經(jīng)驗(yàn)總結(jié):1.盡量減少鎖的使用稻扬,加鎖和解鎖會(huì)有系統(tǒng)開銷蹦哼,臨界區(qū)的代碼是不能并發(fā)執(zhí)行的,進(jìn)入臨界區(qū)次數(shù)頻繁俩檬,線程競爭過于激烈則會(huì)陷入阻塞萎胰,讓出CPU,導(dǎo)致
多次無效的上下文切換棚辽,可使用無鎖隊(duì)列替換技竟。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.明確鎖的范圍,減少鎖的粒度屈藐,減小鎖使用粒度指的是盡量減小鎖作用的臨界區(qū)代碼范圍榔组,臨界區(qū)的代碼范圍越小,多個(gè)線程排隊(duì)進(jìn)入臨界區(qū)的時(shí)間就會(huì)越
短估盘,改寫代碼順序結(jié)構(gòu)瓷患,也可以使用{ }花括號(hào)確定鎖的臨界區(qū)代碼范圍最小化 。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? 3.避免死鎖的出現(xiàn),使用 RAII 技術(shù)將加鎖和解鎖代碼封裝起來,避免出口處沒有解鎖遣妥;線程退出時(shí)一定要及時(shí)釋放持有的鎖擅编;多線程請求鎖的方向一致,比如都是
先請求A鎖箫踩,再請求B鎖爱态,不要出現(xiàn)其他線程先請求B鎖,再請求A鎖境钟;當(dāng)同一線程重復(fù)請求鎖時(shí)锦担,明確是遞增鎖引用計(jì)數(shù),還是會(huì)阻塞抑或是直接獲得鎖慨削。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 4,避免活鎖的出現(xiàn)洞渔,所謂活鎖就是套媚,當(dāng)多個(gè)線程使用 trylock 系列的函數(shù)時(shí),由于多個(gè)線程相互謙讓磁椒,導(dǎo)致即使在某段時(shí)間內(nèi)鎖資源是可用的堤瘤,也可能導(dǎo)致需要
鎖的線程拿不到鎖,因此避免不要過多的線程使用 trylock 請求鎖導(dǎo)致資源浪費(fèi)浆熔。
(2)線程局部存儲(chǔ):每個(gè)線程都擁有自己的局部屬性本辐,并行不悖,互不干擾医增,該屬性會(huì)一直存在直到線程退出慎皱。
? ? ? ? ? 1.? windows下的系統(tǒng)Tls函數(shù)的 API:
? ? ? ? ? ? ? ? ? ? DWORD ? TlsAlloc();? //獲得一個(gè)線程局部存儲(chǔ)塊的索引,調(diào)用失敗返回TLS_OUT_OF_INDEXES(0xFFFFFFFF),否則返回正確索引值
? ? ? ? ? ? ? ? ? ? LPVOID ?? TlsGetValue(DWORD dwTlsIndex); ? //使用索引塊獲取內(nèi)存存儲(chǔ)的數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? BOOL ? ? ?? TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue); // 使用索引塊設(shè)置內(nèi)存存儲(chǔ)數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? BOOL ? ? ?? TlsFree(DWORD dwTlsIndex);? //釋放存儲(chǔ)區(qū)域
? ? ? ?? 此外Microsoft VC++ 編譯器還提供如右所示方法 定義線程局部變量:__declspec(thread)? int? g_mydata = 1;
? ? ? ? ? ? 2. linux下系統(tǒng)的NTPL 提供了API函數(shù):
? ? ? ? ? ? ? ? ? ?? int ? ? pthread_key_create(pthread_key_t* key,void(*destructor)(void*));? //調(diào)用成功會(huì)為線程局部存儲(chǔ)創(chuàng)建一個(gè)新鍵key,根據(jù)key去設(shè)置和獲取數(shù)據(jù)叶骨,參數(shù) destructor 是一個(gè)
自定義函數(shù)指針茫多,void*destructor(void* value){ ? /*多是為了釋放 value 指針指向的資源*/} ,線程終止時(shí)忽刽,如果 key 關(guān)聯(lián)的值不是 NULL地梨,那么 NTPL 會(huì)自動(dòng)執(zhí)行定義的 destructor 函數(shù);
如果無須解構(gòu)缔恳,可以將 destructor 設(shè)置為 NULL宝剖,函數(shù)返回非0表示創(chuàng)建失敗
? ? ? ? ? ? ? ? ? ?? int ? ? pthread_key_delete(pthread_key_t key); //釋放key存儲(chǔ)區(qū)域
? ? ? ? ? ? ? ? ? ?? int ? ? pthread_setspecific(pthread_key_t key,const void*value); //使用Key 設(shè)置存儲(chǔ)數(shù)據(jù)
? ? ? ? ? ? ? ? ? ?? void* pthread_getspecific(pthread_key_t key); //使用key獲得存儲(chǔ)數(shù)據(jù)
? ? ? ? ? ? ? ? ?? 如圖所示,五個(gè)線程 記錄槽位分配情況的數(shù)據(jù)結(jié)構(gòu) pthread_keys 是進(jìn)程唯一的歉甚,pthread_keys 結(jié)構(gòu)示意圖如下:
? ? ? ? ?? 此外也和windows一樣提供方法定于線程局部變量:__thread int val = 0;
? ? ? ? ?? 3. C++ 11 標(biāo)準(zhǔn)提供了一個(gè)新的關(guān)鍵字 thread_local 來定義一個(gè)線程變量万细,可兼容windows和linux運(yùn)行,如 : thread_local int g_mydata = 1; 但需要注意的是纸泄,此關(guān)鍵字需要在
Visual Studio 2015 及以上版本才能支持赖钞。
(3)C庫的非線程安全函數(shù):最初編寫很多 CRT 函數(shù)時(shí),還沒有多線程技術(shù)聘裁,很多函數(shù)內(nèi)部實(shí)現(xiàn)都使用了函數(shù)內(nèi)部的靜態(tài)變量和全局變量雪营。隨著多線程技術(shù)的出現(xiàn),很多函數(shù)出現(xiàn)
了對應(yīng)的多線程安全版本衡便,如localtime-> localtime_r献起、strtok->strtok_r,在這些函數(shù)內(nèi)部很多改用了線程局部存儲(chǔ) 技術(shù)來替代原來使用靜態(tài)變量或者全局變量的做法,所以應(yīng)當(dāng)使用多線程
安全版本函數(shù)替換非線程安全版本函數(shù)。
(4)線程池和隊(duì)列系統(tǒng)的設(shè)計(jì):線程池是一組線程的集合镣陕,當(dāng)程序異步執(zhí)行一些任務(wù)時(shí)谴餐,任務(wù)產(chǎn)生和執(zhí)行是貫穿程序整個(gè)生命周期的,通常需要?jiǎng)?chuàng)建一組在程序生命周期內(nèi)不會(huì)退出的
線程呆抑,為了不浪費(fèi)系統(tǒng)資源岂嗓,與其讓操作系統(tǒng)頻繁地創(chuàng)建和銷毀線程,不如由程序根據(jù)任務(wù)的生產(chǎn)消費(fèi)決定線程的休眠和喚醒鹊碍,因此出現(xiàn)線程池的概念厌殉。在程序生命周期會(huì)有多個(gè)任務(wù)食绿,
任務(wù)存放的地方即為隊(duì)列,此處并不簡單指向一個(gè)list公罕,以是一個(gè)全局變量炫欺、鏈表等,隊(duì)列可以先進(jìn)先出熏兄,單向存儲(chǔ)或者循環(huán)存儲(chǔ),涉及數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)不多描述树姨,隊(duì)列是線程的公共資
源摩桶,多個(gè)線程同時(shí)操作這個(gè)隊(duì)列一般需要加鎖。技術(shù)上除了要解決線程池的創(chuàng)建帽揪、往隊(duì)列中投遞任務(wù)硝清、從隊(duì)列中取任務(wù)處理,還需要做一些善后工作转晰,如線程池的清理芦拿,即如何退出線程
池中的工作線程和清理任務(wù)隊(duì)列。
? ? ? ? ?? 當(dāng)然查邢,隨著技術(shù)的發(fā)展蔗崎,出于復(fù)用和解耦的目的,業(yè)界產(chǎn)生了許多獨(dú)立的隊(duì)列系統(tǒng)扰藕,這些隊(duì)列系統(tǒng)或以一個(gè)獨(dú)立的進(jìn)程運(yùn)行或以支持分布式的一組服務(wù)運(yùn)行缓苛,這種獨(dú)立的隊(duì)
列系統(tǒng)稱之為消息中間件,比如 Kafka邓深、ActiveMQ未桥、RabbitMQ、RocketMQ 等芥备,這些消息中間件在功能上做了豐富的擴(kuò)展冬耿,如消費(fèi)的方式、主備切換萌壳、容災(zāi)容錯(cuò)亦镶,數(shù)據(jù)自動(dòng)備份和過期
數(shù)據(jù)自動(dòng)清理等等,適當(dāng)學(xué)習(xí)使用甚至能了解設(shè)計(jì)思想也是很重要的袱瓮。