并發(fā)的意義就是讓 一個程序同時做多件事情,其目的只是為了能讓程序同時做另一件事情而已,而不是為了讓程序運行的更快(如果是多核處理器,而且任務(wù)可以分成相互獨立的部分,那么并發(fā)確實可以讓事情解決的更快)。
golang從語言級別上對并發(fā)提供了支持,而且在啟動并發(fā)的方式上直接添加了語言級的關(guān)鍵字,不必非要按照固定的格式來定義線程函數(shù),也不必因為啟動線程的時候只能給線程函數(shù)傳遞一個參數(shù)而煩惱。
go的并發(fā)啟動非常簡單,幾乎沒有什么額外的準(zhǔn)備工作,要并發(fā)的函數(shù)和一般的函數(shù)沒有什么區(qū)別,參數(shù)隨意,啟動的時候只需要加一個go關(guān)鍵之即可,其最精髓的部分在于這些協(xié)程(協(xié)程類似于線程,但是是更輕量的線程)的調(diào)度。
package main import ( "fmt" "time" ) func comFunc() { fmt.Println("This is a common function.") } func main() { go comFunc() time.Sleep(time.Second * 3) }
sync包中的WaitGroup實現(xiàn)了一個類似任務(wù)隊列的結(jié)構(gòu),你可以向隊列中加入任務(wù),任務(wù)完成后就把任務(wù)從隊列中移除,如果隊列中的任務(wù)沒有全部完成,隊列就會觸發(fā)阻塞以阻止程序繼續(xù)運行,具體用法參考如下代碼:
package main import ( "fmt" "sync" ) var waitGroup sync.WaitGroup func Afunction(index int) { fmt.Println(index) waitGroup.Done() //任務(wù)完成,將任務(wù)隊列中的任務(wù)數(shù)量-1,其實.Done就是.Add(-1) } func main() { for i := 0; i 10; i++ { waitGroup.Add(1) //每創(chuàng)建一個goroutine,就把任務(wù)隊列中任務(wù)的數(shù)量+1 go Afunction(i) } waitGroup.Wait() //.Wait()這里會發(fā)生阻塞,直到隊列中所有的任務(wù)結(jié)束就會解除阻塞 }
channel是一種golang內(nèi)置的類型,英語的直譯為"通道",其實,它真的就是一根管道,而且是一個先進先出的數(shù)據(jù)結(jié)構(gòu)。
我們能對channel進行的操作只有4種:
(1) 創(chuàng)建chennel (通過make()函數(shù))
(2) 放入數(shù)據(jù) (通過 channel - data 操作)
(3) 取出數(shù)據(jù) (通過 -channel 操作)
(4) 關(guān)閉channel (通過close()函數(shù))
channel的3種性質(zhì)入如下:
(1) channel是一種自動阻塞的管道。
如果管道滿了,一個對channel放入數(shù)據(jù)的操作就會阻塞,直到有某個routine從channel中取出數(shù)據(jù),這個放入數(shù)據(jù)的操作才會執(zhí)行。相反同理,如果管道是空的,一個從channel取出數(shù)據(jù)的操作就會阻塞,直到某個routine向這個channel中放入數(shù)據(jù),這個取出數(shù)據(jù)的操作才會執(zhí)行。這是channel最重要的一個性質(zhì)?。?!
package main func main() { ch := make(chan int, 3) ch - 1 ch - 1 ch - 1 ch - 1 //這一行操作就會發(fā)生阻塞,因為前三行的放入數(shù)據(jù)的操作已經(jīng)把channel填滿了 } package main func main() { ch := make(chan int, 3) -ch //這一行會發(fā)生阻塞,因為channel才剛創(chuàng)建,是空的,沒有東西可以取出 }
(2)channel分為有緩沖的channel和無緩沖的channel。
兩種channel的創(chuàng)建方法如下:
ch := make(chan int) //無緩沖的channel,同等于make(chan int, 0) ch := make(chan int, 5) //一個緩沖區(qū)大小為5的channel
無緩沖通道與有緩沖通道的主要區(qū)別為:無緩沖通道存取數(shù)據(jù)是同步的,即如果通道中無數(shù)據(jù),則通道一直處于阻塞狀態(tài);有緩沖通道存取數(shù)據(jù)是異步的,即存取數(shù)據(jù)互不干擾,只有當(dāng)通道中已滿時,存數(shù)據(jù)操作,通道阻塞;當(dāng)通道中為空時,取數(shù)據(jù)操作,通道阻塞。
因此,使用無緩沖的channel時,放入操作和取出操作不能在同一個routine中,而且應(yīng)該是先確保有某個routine對它執(zhí)行取出操作,然后才能在另一個routine中執(zhí)行放入操作,否則會發(fā)生死鎖現(xiàn)象,示例如下:
package main import ( "fmt" "sync" ) var waitGroup sync.WaitGroup //使用wg等待所有routine執(zhí)行完畢,并輸出相應(yīng)的提示信息 func AFunc(ch chan int) { waitGroup.Add(1) FLAG: for { select { case val := -ch: fmt.Println(val) break FLAG } } waitGroup.Done() fmt.Println("WaitGroup Done") } func main() { ch := make(chan int) //無緩沖通道 execMode := 0 //執(zhí)行模式 0:先啟動并發(fā),正常輸出100 1:后啟動并發(fā),發(fā)生死鎖 switch execMode { case 0: go AFunc(ch) ch - 100 case 1: ch - 100 go AFunc(ch) } waitGroup.Wait() close(ch) }
使用帶緩沖的channel時,因為有緩沖空間,所以只要緩沖區(qū)不滿,放入操作就不會阻塞,同樣,只要緩沖區(qū)不空,取出操作就不會阻塞。
而且,帶有緩沖的channel的放入和取出操作可以用在同一個routine中。
但是,一定要注意放入和取出的速率問題,否則也會發(fā)生死鎖現(xiàn)象,示例如下:
package main import ( "fmt" "sync" ) var waitGroup sync.WaitGroup func AFunc(ch chan int, putMode int) { val := -ch switch putMode { case 0: fmt.Printf("Vaule=%d\n", val) case 1: fmt.Printf("Vaule=%d\n", val) for i := 1; i = 5; i++ { ch - i * val } case 2: fmt.Printf("Vaule=%d\n", val) for i := 1; i = 5; i++ { -ch } } waitGroup.Done() fmt.Println("WaitGroup Done", val) } func main() { ch := make(chan int, 10) putMode := 0 //該模式下,能夠正常輸出所有數(shù)據(jù) //putMode := 1//當(dāng)放入速度遠(yuǎn)大于取數(shù)速度時,程序阻塞 //putMode := 2//當(dāng)取數(shù)速度遠(yuǎn)大于放數(shù)速度時,程序阻塞 for i := 0; i 1000; i++ { ch - i waitGroup.Add(1) go AFunc(ch, putMode) } waitGroup.Wait() close(ch) }
(3)關(guān)閉后的channel可以取數(shù)據(jù),但是不能放數(shù)據(jù)。
而且,channel在執(zhí)行了close()后并沒有真的關(guān)閉,channel中的數(shù)據(jù)全部取走之后才會真正關(guān)閉。
package main func main() { ch := make(chan int, 5) ch - 1 ch - 1 close(ch) ch - 1 //不能對關(guān)閉的channel執(zhí)行放入操作 // 會觸發(fā)panic }
package main func main() { ch := make(chan int, 5) ch - 1 ch - 1 close(ch) -ch //只要channel還有數(shù)據(jù),就可能執(zhí)行取出操作 //正常結(jié)束 }
package main import "fmt" func main() { ch := make(chan int, 5) ch - 1 ch - 1 ch - 1 ch - 1 close(ch) //如果執(zhí)行了close()就立即關(guān)閉channel的話,下面的循環(huán)就不會有任何輸出了 for { data, ok := -ch if !ok { break } fmt.Println(data) } // 輸出: // 1 // 1 // 1 // 1 // // 調(diào)用了close()后,只有channel為空時,channel才會真的關(guān)閉 }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
標(biāo)簽:銅川 重慶 梅河口 蘭州 雞西 吐魯番 汕頭 欽州
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《關(guān)于golang高并發(fā)的實現(xiàn)與注意事項說明》,本文關(guān)鍵詞 關(guān)于,golang,高并發(fā),高,并發(fā),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。