GO并發(fā)
使用goroutine運(yùn)行程序,檢測(cè)并修正狀態(tài),利用通道共享數(shù)據(jù)。
通常程序會(huì)被編寫為一個(gè)順序執(zhí)行并完成一個(gè)獨(dú)立任務(wù)的代碼。
web服務(wù)器需要在各自獨(dú)立的套接字(Socket)上通過接受多個(gè)數(shù)據(jù)請(qǐng)求。
每個(gè)套接字請(qǐng)求是獨(dú)立的,獨(dú)立于其他套接字進(jìn)行處理。并行執(zhí)行多個(gè)請(qǐng)求的能力可顯著提高這類系統(tǒng)的性能。
Go語(yǔ)言中的并發(fā)指的是能讓某個(gè)函數(shù)獨(dú)立于其他函數(shù)運(yùn)行的能力。當(dāng)一個(gè)函數(shù)創(chuàng)建為goroutine時(shí),Go會(huì)將其視為一個(gè)獨(dú)立的工作單元。這個(gè)單元會(huì)被調(diào)度到可用的邏輯處理器上執(zhí)行。
Go語(yǔ)言運(yùn)行時(shí)的調(diào)度器,管理創(chuàng)建的所有g(shù)oroutine并為其分配執(zhí)行時(shí)間。
這個(gè)調(diào)度器在操作系統(tǒng)之上,將操作系統(tǒng)的線程與語(yǔ)言運(yùn)行時(shí)的邏輯處理器綁定,并在邏輯處理器上運(yùn)行
goroutine。調(diào)度器在任何給定的時(shí)間,都會(huì)全面控制哪個(gè)goroutine 要在哪個(gè)邏輯處理器上運(yùn)行。
Go語(yǔ)言的并發(fā)同步模型來自一個(gè)叫作通信順序進(jìn)程(Communicating Sequential Processes,CSP)
的范型(paradigm)。CSP 是一種消息傳遞模型,通過在goroutine 之間傳遞數(shù)據(jù)來傳遞消息,而不是
對(duì)數(shù)據(jù)進(jìn)行加鎖來實(shí)現(xiàn)同步訪問。用于在goroutine 之間同步和傳遞數(shù)據(jù)的關(guān)鍵數(shù)據(jù)類型叫作通道
(channel)。
使用通道可以使編寫并發(fā)程序更容易,也能夠讓并發(fā)程序出錯(cuò)更少。
什么是操作系統(tǒng)的線程(thread)和進(jìn)程(process)
便于理解Go語(yǔ)言運(yùn)行時(shí)調(diào)度器如何利用操作系統(tǒng)來并發(fā)運(yùn)行g(shù)oroutine。
當(dāng)運(yùn)行一個(gè)應(yīng)用程序的時(shí)候,操作系統(tǒng)會(huì)為這個(gè)應(yīng)用程序啟動(dòng)一個(gè)進(jìn)程。可以將這個(gè)進(jìn)程看作一個(gè)包含了應(yīng)用程序在運(yùn)行中需要用到和維護(hù)的各種資源的容器。
一個(gè)線程是一個(gè)執(zhí)行空間,這個(gè)空間會(huì)被操作系統(tǒng)調(diào)度來運(yùn)行函數(shù)中所寫的代碼。
每個(gè)進(jìn)程至少包含一個(gè)線程,每個(gè)進(jìn)程的初始線程被稱作主線程。
因?yàn)閳?zhí)行這個(gè)線程的空間是應(yīng)用程序的本身的空間,所以當(dāng)主線程終止時(shí),應(yīng)用程序也會(huì)終止。
操作系統(tǒng)將線程調(diào)度到某個(gè)處理器上運(yùn)行,這個(gè)處理器并不一定是進(jìn)程所在的處理器。
不同操作系統(tǒng)使用的線程調(diào)度算法一般都不一樣,但是這種不同會(huì)被操作系統(tǒng)屏蔽,并不會(huì)展示給程序員。
進(jìn)程維護(hù)了應(yīng)用程序運(yùn)行時(shí)的內(nèi)存地址空間、文件和設(shè)備的句柄以及線程。操作系統(tǒng)的調(diào)度器決定哪個(gè)線程會(huì)獲得給定的CPU的運(yùn)行時(shí)間。
操作系統(tǒng)會(huì)在物理處理器上調(diào)度線程來運(yùn)行,而Go 語(yǔ)言的運(yùn)行時(shí)會(huì)在邏輯處理器上調(diào)度goroutine來運(yùn)行。
每個(gè)邏輯處理器都分別綁定到單個(gè)操作系統(tǒng)線程。操作系統(tǒng)線程、邏輯處理器和本地運(yùn)行隊(duì)列之間的關(guān)系。
如果創(chuàng)建一個(gè)goroutine 并準(zhǔn)備運(yùn)行,這個(gè)goroutine 就會(huì)被放到調(diào)度器的全局運(yùn)行隊(duì)列中。之后,調(diào)度器就
將這些隊(duì)列中的goroutine 分配給一個(gè)邏輯處理器,并放到這個(gè)邏輯處理器對(duì)應(yīng)的本地運(yùn)行隊(duì)列上,Go語(yǔ)言的
運(yùn)行時(shí)默認(rèn)會(huì)為每個(gè)可用的物理處理器分配一個(gè)邏輯處理器。
本地運(yùn)行隊(duì)列中的goroutine會(huì)一直等待直到自己被分配的邏輯處理器執(zhí)行。
go調(diào)度器如何管理goroutine:
Go語(yǔ)言運(yùn)行時(shí)會(huì)把goroutine調(diào)度到邏輯處理器上運(yùn)行。這個(gè)邏輯處理器綁定到唯一的操作系統(tǒng)線程。當(dāng)goroutine可以運(yùn)行的時(shí)候,會(huì)被放入邏輯處理器的執(zhí)行隊(duì)列中。
當(dāng)goroutine執(zhí)行了一個(gè)阻塞的系統(tǒng)調(diào)用時(shí),調(diào)度器會(huì)將這個(gè)線程與處理器分類,并創(chuàng)建一個(gè)新線程來運(yùn)行這個(gè)處理器上提供的服務(wù)。
有時(shí),正在運(yùn)行的goroutine 需要執(zhí)行一個(gè)阻塞的系統(tǒng)調(diào)用,如打開一個(gè)文件。當(dāng)這類調(diào)用
發(fā)生時(shí),線程和goroutine 會(huì)從邏輯處理器上分離,該線程會(huì)繼續(xù)阻塞,等待系統(tǒng)調(diào)用的返回。
與此同時(shí),這個(gè)邏輯處理器就失去了用來運(yùn)行的線程。所以,調(diào)度器會(huì)創(chuàng)建一個(gè)新線程,并將其
綁定到該邏輯處理器上。之后,調(diào)度器會(huì)從本地運(yùn)行隊(duì)列里選擇另一個(gè)goroutine 來運(yùn)行。一旦
被阻塞的系統(tǒng)調(diào)用執(zhí)行完成并返回,對(duì)應(yīng)的goroutine 會(huì)放回到本地運(yùn)行隊(duì)列,而之前的線程會(huì)
保存好,以便之后可以繼續(xù)使用。
如果一個(gè) goroutine 需要做一個(gè)網(wǎng)絡(luò)I/O 調(diào)用,流程上會(huì)有些不一樣。在這種情況下,goroutine
會(huì)和邏輯處理器分離,并移到集成了網(wǎng)絡(luò)輪詢器的運(yùn)行時(shí)。一旦該輪詢器指示某個(gè)網(wǎng)絡(luò)讀或者寫
操作已經(jīng)就緒,對(duì)應(yīng)的goroutine 就會(huì)重新分配到邏輯處理器上來完成操作。調(diào)度器對(duì)可以創(chuàng)建
的邏輯處理器的數(shù)量沒有限制,但語(yǔ)言運(yùn)行時(shí)默認(rèn)限制每個(gè)程序最多創(chuàng)建10 000 個(gè)線程。這個(gè)
限制值可以通過調(diào)用runtime/debug 包的SetMaxThreads 方法來更改。如果程序試圖使用
更多的線程,就會(huì)崩潰。
并發(fā)(concurrency)不是并行(parallelism)。并行是讓不同的代碼片段同時(shí)在不同的物理處
理器上執(zhí)行。并行的關(guān)鍵是同時(shí)做很多事情,而并發(fā)是指同時(shí)管理很多事情,這些事情可能只做
了一半就被暫停去做別的事情了。在很多情況下,并發(fā)的效果比并行好,因?yàn)椴僮飨到y(tǒng)和硬件的
總資源一般很少,但能支持系統(tǒng)同時(shí)做很多事情。這種“使用較少的資源做更多的事情”的哲學(xué),
也是指導(dǎo)Go 語(yǔ)言設(shè)計(jì)的哲學(xué)。
如果希望讓goroutine 并行,必須使用多于一個(gè)邏輯處理器。當(dāng)有多個(gè)邏輯處理器時(shí),調(diào)度器
會(huì)將goroutine 平等分配到每個(gè)邏輯處理器上。這會(huì)讓goroutine 在不同的線程上運(yùn)行。不過要想真
的實(shí)現(xiàn)并行的效果,用戶需要讓自己的程序運(yùn)行在有多個(gè)物理處理器的機(jī)器上。否則,哪怕Go 語(yǔ)
言運(yùn)行時(shí)使用多個(gè)線程,goroutine 依然會(huì)在同一個(gè)物理處理器上并發(fā)運(yùn)行,達(dá)不到并行的效果。
在一個(gè)邏輯處理器上并發(fā)運(yùn)行g(shù)oroutine 和在兩個(gè)邏輯處理器上并行運(yùn)行兩個(gè)并
發(fā)的goroutine 之間的區(qū)別。