Golang的协程(goroutine)和同步机制
1、協(xié)程介紹
????????進(jìn)程和線程都是由操作系統(tǒng)內(nèi)核進(jìn)行調(diào)度,有 CPU 時間片的概念,進(jìn)行搶占式調(diào)度。
????????協(xié)程是用戶級的線程,對內(nèi)核是透明的,系統(tǒng)并不知道協(xié)程的存在,并且協(xié)程是非搶占式調(diào)度,無法實現(xiàn)公平的任務(wù)調(diào)用,通常只進(jìn)行協(xié)作式調(diào)度,需要協(xié)程自己主動把控制權(quán)轉(zhuǎn)讓出去之后,其他協(xié)程才能被執(zhí)行到。
在任務(wù)調(diào)度上,協(xié)程弱于線程;
在資源消耗上,協(xié)程則是極低的,一個線程的內(nèi)存在 MB 級別,而協(xié)程只需要 KB 級別。
2、協(xié)程的的基本原理
????????協(xié)程是基于線程的,二者的原理一樣,線程是在操作系統(tǒng)內(nèi)核層面實現(xiàn)的,協(xié)程是在應(yīng)用層實現(xiàn)了線程。
????????Go Scheduler會把goroutine調(diào)度到邏輯處理器上運行,邏輯處理器會一對一的綁定到操作系統(tǒng)的線程。當(dāng)goroutine可以運行時,會被放入一個邏輯處理器的待執(zhí)行隊列中;當(dāng)goroutine遇到長時間執(zhí)行或執(zhí)行了一個阻塞的系統(tǒng)調(diào)用時(如打開文件),Go Scheduler會將這個邏輯處理器與線程分離,并將另一個線程綁定到這個邏輯處理器,之后從待執(zhí)行隊列中選擇下一個goroutine來運行,原來的goroutine保存到待執(zhí)行隊列等待調(diào)用(邏輯處理器是不動的)。
????????每一個goroutine是一個獨立的執(zhí)行單元,相較于每個OS線程固定分配2M內(nèi)存的模式,goroutine的棧采取了動態(tài)擴容方式, 初始時僅為2KB,隨著任務(wù)執(zhí)行按需增長,最大可達(dá)1GB,且由golang的調(diào)度器 Go Scheduler 來調(diào)度。此外,GC還會周期性地將不再使用的內(nèi)存回收,收縮棧空間。
????????Go Scheduler調(diào)度器能管理所有被創(chuàng)建的 goroutine 并為其分配執(zhí)行時間。Go Scheduler能將語言運行時的邏輯處理器與操作系統(tǒng)的線程一一綁定,并會全面的控制哪個 goroutine 要在哪個邏輯處理器上運行。
3、Goroutine同步機制
Goroutine提供了一種傳統(tǒng)的同步機制——對共享資源加鎖。
(1)原子函數(shù)能夠以很底層的加鎖機制來同步訪問整型變量和指針。
atomic.AddInt64(&counter, 1),強制同一時刻只能有一個 goroutine 運行并完成這個加法操作,還有LoadInt、StoreInt等。
(2)互斥鎖用于在代碼上創(chuàng)建一個臨界區(qū),保證同一時間只有一個 goroutine 可以
執(zhí)行這個臨界區(qū)里的代碼。
(3)通道
使用原子函數(shù)和互斥鎖能夠保證對共享資源的安全訪問以及消除競爭狀態(tài);
使用通道,通過發(fā)送和接收需要共享的資源,在goroutine 之間進(jìn)行同步。
無緩沖的通道(unbuffered channel):指在接收前沒有能力保存任何值的通道。這種類型的通
道要求發(fā)送goroutine 和接收goroutine 同時準(zhǔn)備好,才能完成發(fā)送和接收操作,如果兩個goroutine
沒有同時準(zhǔn)備好,通道會導(dǎo)致先執(zhí)行發(fā)送或接收操作的goroutine阻塞等待。
有緩沖的通道(buffered channel):是一種在被接收前能存儲一個或者多個值的通道。不強制要求goroutine 之間必須同時完成發(fā)送和接收。只有在通道沒有可用緩沖區(qū)容納被發(fā)送的值時,發(fā)送動作才會阻塞;只有在通道中沒有要接收的值時,接收動作才會阻塞。
4、多線程-〉異步編程-〉協(xié)程
????????線程是操作系統(tǒng)的內(nèi)核對象,多線程編程時,如果線程數(shù)過多,就會導(dǎo)致頻繁的上下文切換,這些 cpu 時間是一個額外的耗費。
????????于是操作系統(tǒng)提供了基于事件模式的異步編程模型,用少量的線程來服務(wù)大量的網(wǎng)絡(luò)連接和I/O操作,但是采用異步和基于事件的編程模型,復(fù)雜化了程序代碼的編寫,非常容易出錯,再加上線程穿插,也提高排查錯誤的難度。
????????協(xié)程是在應(yīng)用層模擬的線程,避免了上下文切換的額外耗費,兼顧了多線程的優(yōu)點,簡化了高并發(fā)程序的復(fù)雜度。
5、協(xié)程為什么大熱?
????????主要用于網(wǎng)絡(luò)編程,其獨有的特點。高并發(fā)(每秒鐘上千數(shù)萬的單機訪問量),程序生命期短(毫秒,秒級) 高IO,低計算(連接數(shù)據(jù)庫,請求API)。
6、實際應(yīng)用
????????在一個函數(shù)調(diào)用前加上go關(guān)鍵字,這次調(diào)用就會在一個新的goroutine中并發(fā)執(zhí)行,當(dāng)被調(diào)用的函數(shù)返回時,這個goroutine也自動結(jié)束。需要注意的是,如果這個函數(shù)有返回值,那么這個返回值會被丟棄。
????????
總結(jié)
以上是生活随笔為你收集整理的Golang的协程(goroutine)和同步机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是 “马太效应” ?
- 下一篇: 耗费 7.5 亿做的“垃圾”,被 3 个