程與線程的本質(zhì)區(qū)別及實(shí)際應(yīng)用場(chǎng)景)
1. 進(jìn)程與線程的本質(zhì)區(qū)別在計(jì)算機(jī)科學(xué)領(lǐng)域進(jìn)程和線程是操作系統(tǒng)資源分配和調(diào)度的基本單位。很多開發(fā)者雖然經(jīng)常使用這兩個(gè)概念但對(duì)它們的本質(zhì)區(qū)別理解并不透徹。我結(jié)合自己多年系統(tǒng)開發(fā)經(jīng)驗(yàn)從五個(gè)維度幫你徹底理清二者的差異。1.1 資源分配方式進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單位。當(dāng)啟動(dòng)一個(gè)程序時(shí)操作系統(tǒng)會(huì)為其創(chuàng)建一個(gè)獨(dú)立的進(jìn)程分配獨(dú)立的內(nèi)存空間包括代碼段、數(shù)據(jù)段、堆棧等、文件描述符、信號(hào)處理等系統(tǒng)資源。這種隔離性使得不同進(jìn)程之間不會(huì)相互干擾但也帶來(lái)了較高的資源開銷。線程則是在進(jìn)程內(nèi)部執(zhí)行的更小單位一個(gè)進(jìn)程可以包含多個(gè)線程。關(guān)鍵區(qū)別在于同一進(jìn)程內(nèi)的所有線程共享該進(jìn)程的內(nèi)存空間和系統(tǒng)資源。這意味著線程可以直接訪問(wèn)進(jìn)程的全局變量線程間通信更高效不需要IPC機(jī)制創(chuàng)建和銷毀線程的開銷遠(yuǎn)小于進(jìn)程實(shí)際開發(fā)中常見誤區(qū)認(rèn)為線程也擁有獨(dú)立的內(nèi)存空間。實(shí)際上線程只有獨(dú)立的??臻g用于存儲(chǔ)局部變量而堆和全局變量是共享的。1.2 調(diào)度與執(zhí)行方式現(xiàn)代操作系統(tǒng)采用分時(shí)技術(shù)實(shí)現(xiàn)多任務(wù)CPU通過(guò)快速切換來(lái)同時(shí)執(zhí)行多個(gè)進(jìn)程或線程。但二者的調(diào)度機(jī)制有本質(zhì)不同進(jìn)程調(diào)度由操作系統(tǒng)內(nèi)核全權(quán)負(fù)責(zé)采用搶占式調(diào)度。每個(gè)進(jìn)程都有獨(dú)立的地址空間切換時(shí)需要保存/恢復(fù)完整的上下文包括內(nèi)存映射、寄存器狀態(tài)等這稱為進(jìn)程上下文切換開銷較大。線程調(diào)度分為兩種模式用戶級(jí)線程由線程庫(kù)如pthread管理內(nèi)核無(wú)感知內(nèi)核級(jí)線程由操作系統(tǒng)內(nèi)核直接管理在Linux中通過(guò)clone()系統(tǒng)調(diào)用創(chuàng)建線程與進(jìn)程共享地址空間但擁有獨(dú)立的task_struct1.3 通信機(jī)制對(duì)比由于內(nèi)存隔離進(jìn)程間通信(IPC)必須通過(guò)操作系統(tǒng)提供的機(jī)制管道(Pipe)消息隊(duì)列共享內(nèi)存信號(hào)量Socket而線程間通信簡(jiǎn)單得多可以直接讀寫全局變量或通過(guò)線程同步機(jī)制互斥鎖、條件變量等協(xié)調(diào)訪問(wèn)。這也是多線程程序更容易出現(xiàn)競(jìng)態(tài)條件的原因。1.4 容錯(cuò)性差異進(jìn)程的隔離性帶來(lái)了更好的容錯(cuò)能力。一個(gè)進(jìn)程崩潰通常不會(huì)影響其他進(jìn)程除非是父進(jìn)程終止導(dǎo)致子進(jìn)程被牽連。線程則不同由于共享地址空間單個(gè)線程崩潰如段錯(cuò)誤會(huì)導(dǎo)致整個(gè)進(jìn)程終止線程間的內(nèi)存錯(cuò)誤可能相互影響如堆內(nèi)存破壞1.5 性能開銷實(shí)測(cè)通過(guò)簡(jiǎn)單的基準(zhǔn)測(cè)試可以直觀感受二者的性能差異測(cè)試環(huán)境Linux 5.15, 8核CPU操作類型創(chuàng)建時(shí)間(μs)上下文切換時(shí)間(μs)進(jìn)程12003.5線程501.2這個(gè)差距在需要頻繁創(chuàng)建/銷毀執(zhí)行單元的場(chǎng)景如Web服務(wù)器會(huì)非常明顯。2. 實(shí)際應(yīng)用場(chǎng)景選擇理解了理論區(qū)別后我們來(lái)看如何在實(shí)際項(xiàng)目中做出合理選擇。2.1 何時(shí)使用多進(jìn)程適合多進(jìn)程的場(chǎng)景特征需要高可靠性如關(guān)鍵服務(wù)進(jìn)程各任務(wù)間需要強(qiáng)隔離如安全沙箱任務(wù)本身是CPU密集型且可并行化需要利用多機(jī)分布式能力典型案例Chrome瀏覽器每個(gè)標(biāo)簽頁(yè)獨(dú)立進(jìn)程N(yùn)ginxWorker進(jìn)程模型科學(xué)計(jì)算MPI并行任務(wù)2.2 何時(shí)使用多線程適合多線程的場(chǎng)景特征任務(wù)需要頻繁共享數(shù)據(jù)存在大量IO等待如網(wǎng)絡(luò)服務(wù)需要快速響應(yīng)事件運(yùn)行在單機(jī)環(huán)境典型案例GUI應(yīng)用程序主線程UI后臺(tái)工作線程游戲服務(wù)器一個(gè)玩家一個(gè)線程數(shù)據(jù)庫(kù)連接池2.3 混合模型實(shí)踐現(xiàn)代高性能應(yīng)用常采用混合模式Nginx多進(jìn)程每個(gè)進(jìn)程內(nèi)多線程Redis單進(jìn)程多IO線程后臺(tái)線程MySQL連接線程池后臺(tái)清理進(jìn)程這種架構(gòu)既能利用多核CPU又能減少上下文切換開銷。3. 常見問(wèn)題深度解析3.1 資源占用異常問(wèn)題從熱搜詞可以看到很多關(guān)于進(jìn)程/線程資源占用的問(wèn)題比如win10的system進(jìn)程占用高fpm進(jìn)程cpu占用高xorg進(jìn)程占用很高這類問(wèn)題的排查思路使用top/htop查看實(shí)時(shí)資源占用通過(guò)strace/pstack分析調(diào)用棧檢查是否有死循環(huán)或阻塞操作對(duì)于線程問(wèn)題可用perf工具采樣PHP-FPM進(jìn)程CPU高的典型原因阻塞在某個(gè)外部調(diào)用如數(shù)據(jù)庫(kù)查詢或者存在低效的正則表達(dá)式。3.2 進(jìn)程通信問(wèn)題熱搜中反映的典型IPC問(wèn)題c# 復(fù)制文件時(shí)出現(xiàn)正由另一進(jìn)程使用進(jìn)程通信ipc解決方案文件鎖沖突使用flock()實(shí)現(xiàn)協(xié)同訪問(wèn)共享內(nèi)存同步配合信號(hào)量使用跨語(yǔ)言通信考慮用gRPC或消息隊(duì)列3.3 線程安全實(shí)踐多線程編程中最棘手的就是線程安全問(wèn)題。從熱搜c# 正由另一進(jìn)程使用可以看出這類問(wèn)題的普遍性。保證線程安全的幾種方法互斥鎖pthread_mutex原子操作C11 atomic線程局部存儲(chǔ)__thread無(wú)鎖數(shù)據(jù)結(jié)構(gòu)如CAS實(shí)現(xiàn)隊(duì)列4. 操作系統(tǒng)實(shí)現(xiàn)差異不同操作系統(tǒng)對(duì)進(jìn)程/線程的實(shí)現(xiàn)方式各有特點(diǎn)。4.1 Linux的實(shí)現(xiàn)Linux使用輕量級(jí)進(jìn)程實(shí)現(xiàn)線程通過(guò)clone()系統(tǒng)調(diào)用創(chuàng)建共享同一內(nèi)存空間的task_struct即為線程線程組共享tgid即進(jìn)程ID查看線程的方法ps -eLf | grep [進(jìn)程名] top -H -p [PID]4.2 Windows的實(shí)現(xiàn)Windows采用更明確的線程對(duì)象CreateProcess創(chuàng)建進(jìn)程CreateThread創(chuàng)建線程線程有獨(dú)立的THREADENTRY32結(jié)構(gòu)4.3 編程語(yǔ)言抽象各語(yǔ)言對(duì)線程的封裝層次不同Java完全屏蔽OS差異Thread類統(tǒng)一接口Python由于GIL存在多線程只適合IO密集型任務(wù)Go輕量級(jí)goroutineM:N調(diào)度模型5. 性能優(yōu)化實(shí)戰(zhàn)技巧5.1 減少上下文切換高頻切換會(huì)顯著降低性能優(yōu)化方法設(shè)置線程親和性affinity使用線程池避免頻繁創(chuàng)建銷毀調(diào)整Linux調(diào)度策略sched_setscheduler5.2 避免虛假共享雖然線程共享地址空間但CPU緩存是以緩存行為單位通常64字節(jié)。當(dāng)不同線程修改同一緩存行的不同變量時(shí)會(huì)導(dǎo)致緩存失效這種現(xiàn)象稱為虛假共享。解決方法對(duì)齊關(guān)鍵變量到緩存行大小使用padding填充結(jié)構(gòu)體將頻繁寫的變量隔離到獨(dú)立緩存行5.3 鎖優(yōu)化策略不合理的鎖使用會(huì)導(dǎo)致性能瓶頸減小臨界區(qū)范圍使用讀寫鎖替代互斥鎖嘗試無(wú)鎖編程如RCU避免鎖嵌套6. 現(xiàn)代發(fā)展趨勢(shì)隨著硬件發(fā)展進(jìn)程/線程模型也在演進(jìn)6.1 協(xié)程的興起協(xié)程Coroutine比線程更輕量用戶態(tài)調(diào)度切換開銷極小同步編程風(fēng)格異步執(zhí)行效果典型實(shí)現(xiàn)Python asyncio、Go goroutine6.2 異構(gòu)計(jì)算GPU、TPU等加速器引入新的執(zhí)行模型CUDA網(wǎng)格-塊-線程三級(jí)結(jié)構(gòu)OpenCL工作組-工作項(xiàng)劃分需要特殊的同步機(jī)制6.3 服務(wù)網(wǎng)格在微服務(wù)架構(gòu)下服務(wù)間的通信模式演變?yōu)镾idecar代理進(jìn)程基于gRPC的線程池異步事件驅(qū)動(dòng)模型理解進(jìn)程和線程的底層區(qū)別能幫助我們?cè)诓煌瑘?chǎng)景做出合理架構(gòu)選擇。實(shí)際開發(fā)中沒有絕對(duì)的好壞關(guān)鍵是根據(jù)業(yè)務(wù)特點(diǎn)找到平衡點(diǎn)。