天天看點

《Go語言程式設計》-并發程式設計

整理自《go語言程式設計》-第四章

1、并發基礎

多程序:多程序是在作業系統層面進行并發的基本模式。同時也是開銷最大的模式。在Linux平台上,很過工具鍊正是采用這種模式在工作。比如某個Web伺服器,它會有專門的程序負責網絡端口的監聽和連結管理,還會有專門的程序負責事務和運算。這種方法的好處在于簡單、程序間互不影響,壞處是系統開銷大,因為所有的程序都是由核心管理的。

多線程:多線程在大部分作業系統上都屬于系統層面的并發模式,也是我們使用最多的最有效的一種模式。目前,我們所見的幾乎所有工具鍊都會使用這種模式。它比多程序的開銷小很多,但是其開銷依舊比較大,且在高并發模式下,效率會有影響。

基于回調的非阻塞/異步IO:這種架構的誕生實際上來源于月多線程模式的危機。在很多高并發伺服器開發實踐中,使用多線程模式會很快耗盡伺服器的記憶體和CPU資源。二這種模式通過事件驅動的方式使用異步IO,伺服器持續運轉,且盡可能少用線程,降低開銷,它目前在Node.js中得到了很好的實踐,但是使用這種模式,程式設計比多線程要複雜,因為它把流程做了分割,對于問題的本身反應不夠自然。

協程:協程(coroutine)本質上一種使用者态線程,不需要作業系統來進行搶占式排程,且在真正的實作中寄存于線程中,是以,系統開銷極小,可以有效提高線程的任務并發性,而避免多線程的缺點。使用協程的優點是程式設計簡單,結構清晰;缺點是需要語言的支援,如果不支援,則需要使用者在程式中自行實作排程器。

2、協程

執行體是個抽象的概念,在作業系統層面有多個概念與之對應,比如作業系統自己掌握的程序(process)、程序内的線程(thread)以及程序内的協程(coroutine,也叫輕量級線程)

多數語言在文法層面并不直接支援協程,而是通過庫的方式支援。

Go語言在語言級别支援輕量級線程,叫goroutine。

3、Goroutine

Goroutine是Go語言中的輕量級線程實作,由Go運作時(runtime)管理。

.go:關鍵字,函數調用前加go關鍵字,這次調用就會在一個新的goroutine中并發執行。當被調用的函數傳回時,這個goroutine也自動結束。如果不是使用channel接收的話,這個函數的傳回值将會被丢棄。

4、并發通信

不管是什麼平台、什麼程式設計語言并發都是一個大話題。

在工程上,有兩種最常見的并發通信模型:共享資料和消息。

共享資料:多個并發單元分别儲存對同一個資料的引用,實作對該資料的共享。被共享的資料可能有多種形式,比如記憶體資料塊、磁盤檔案、網絡資料等。在實際工程應用中最常見的就是共享記憶體。

Go語言社群:不要通過共享記憶體來通信,而應該通過通信來共享記憶體。

Go語言提供的是另一種通信模型,即以消息機制而非共享記憶體作為通信方式。

5、Channel

(1)概念

Channel 是Go語言在語言級别提供的goroutine間的通信方式。我們可以使用channel在兩個或多個goroutine之間傳遞消息。

Channel是程序内的通信方式,是以通過channel傳遞對象的過程和調用函數時參數

傳遞的行為比較一緻,比如可以傳遞指針等。

Channel是類型相關的,一個channel隻能傳遞一種類型的值。

(2)基本文法

聲明形式:

Var chanName chan ElementType

例子:var ch chan int

定義channel:

Ch := make(chan int)

向channel中寫入資料:

Ch <- value

從channel中讀取資料:

Value := <- ch

(3)select

Go語言直接在語言級别支援select關鍵字,用于處理異步IO問題。

Select {

Case <- chan1:

//如果chan1成功讀到資料,則進行該case處理語句

Case chan2 <-1:

//如果成功向chan2寫入資料,則進行該case處理語句

Default:

//如果上面的都沒有成功,則進入default處理流程

}

(4)緩沖機制

建立一個帶緩沖區的channel:

C := make(chan int, 1024)

(5)逾時機制

在并發程式設計的通信過程中,最需要處理的就是逾時問題,即向channel寫入資料時發現channel已滿,或者從channel試圖讀取資料時發現channel為空。如果不正确處理這個問題,可能會導緻整個goroutinue鎖死。

Go語言沒有提供直接的逾時處理機制,但可以利用select機制,可以解決逾時問題。

《Go語言程式設計》-并發程式設計

(6)channel的傳遞

注意:在go語言中channel本身也是一個原生類型,與map之類的類型地位一樣,是以channel本身在定義後也可以通過channl來傳遞。

(7)單向channel

單向channel隻能用于發送或者接收資料。

從設計者的角度,所有的代碼都應該遵循“最小權限的原則”

(8)關閉channel

使用go語言的内置函數:close(ch)

在讀取時使用多重傳回值的方式可以判斷: x, ok := <-ch

6、讓出時間片

每個goroutine中控制何時主動讓出時間片給其他goroutine,可以使用runtime包中的Gosched()函數實作。

7、同步

Go 語言包中的sync包提供兩種鎖類型:sync.Mutex 和sync.RWMutex.

8、全局唯一性操作

Go語言提供一個Once類型來保證全局唯一性操作。