Channel使用技巧
前言
Go協(xié)程一般使用channel(通道)通信從而協(xié)調(diào)/同步他們的工作。合理利用Go協(xié)程和channel能幫助我們大大提高程序的性能。本文將介紹一些使用channel的場景及技巧
場景一,使用channel返回運(yùn)算結(jié)果
計(jì)算斐波那契數(shù)列,在學(xué)習(xí)遞歸時(shí)候這是個(gè)經(jīng)典問題。現(xiàn)在我們不用遞歸實(shí)現(xiàn),而是用channel返回計(jì)算得出的斐波那契數(shù)列。 計(jì)算前40個(gè)斐波那契數(shù)列的值,看下效率
package mainimport ("fmt""time" ) //計(jì)算斐波那契數(shù)列并寫到ch中 func fibonacci(n int, ch chan<- int) {first, second := 1, 1for i := 0; i < n; i++ {ch <- firstfirst, second = second, first+second}close(ch) }func main() {ch := make(chan int, 40)i := 0start := time.Now()go fibonacci(cap(ch), ch)for result := range ch {fmt.Printf("fibonacci(%d) is: %d\n", i, result)i++}end := time.Now()delta := end.Sub(start)fmt.Printf("took the time: %s\n", delta) }只花了7ms,效率是遞歸實(shí)現(xiàn)的100倍(主要是算法效率問題)
fibonacci(33) is: 5702887 fibonacci(34) is: 9227465 fibonacci(35) is: 14930352 fibonacci(36) is: 24157817 fibonacci(37) is: 39088169 fibonacci(38) is: 63245986 fibonacci(39) is: 102334155 took the time: 8.0004ms使用for-range讀取channel返回的結(jié)果十分便利。當(dāng)channel關(guān)閉且沒有數(shù)據(jù)時(shí),for循環(huán)會自動退出,無需主動監(jiān)測channel是否關(guān)閉。close(ch)只針對寫數(shù)據(jù)到channel起作用,意思是close(ch)后,ch中不能再寫數(shù)據(jù),但不影響從ch中讀數(shù)據(jù)
場景二,使用channel獲取多個(gè)并行方法中的一個(gè)結(jié)果
假設(shè)程序從多個(gè)復(fù)制的數(shù)據(jù)庫同時(shí)讀取。只需要接收首先到達(dá)的一個(gè)答案,Query 函數(shù)獲取數(shù)據(jù)庫的連接切片并請求。并行請求每一個(gè)數(shù)據(jù)庫并返回收到的第一個(gè)響應(yīng):
func Query(conns []conn, query string) Result {ch := make(chan Result, 1)for _, conn := range conns {go func(c Conn) {select {case ch <- c.DoQuery(query):}}(conn)}return <- ch }場景三,響應(yīng)超時(shí)處理
在調(diào)用遠(yuǎn)程方法的時(shí)候,存在超時(shí)可能,超時(shí)后返回超時(shí)提示
func CallWithTimeOut(timeout time.Duration) (int, error) {select {case resp := <-Call():return resp, nilcase <-time.After(timeout):return -1, errors.New("timeout")} }func Call() <-chan int {outCh := make(chan int)go func() {//調(diào)用遠(yuǎn)程方法}()return outCh }同樣可以擴(kuò)展到channel的讀寫操作
func ReadWithTimeOut(ch <-chan int) (x int, err error) {select {case x = <-ch:return x, nilcase <-time.After(time.Second):return 0, errors.New("read time out")} } func WriteWithTimeOut(ch chan<- int, x int) (err error) {select {case ch <- x:return nilcase <-time.After(time.Second):return errors.New("read time out")} }使用<-time.After()超時(shí)設(shè)置可能引發(fā)的內(nèi)存泄露問題,可以看這篇文章
場景四,多任務(wù)并發(fā)執(zhí)行和順序執(zhí)行
方法A和B同時(shí)執(zhí)行,方法C等待方法A執(zhí)行完后才能執(zhí)行,main等待A、B、C執(zhí)行完才退出
package mainimport ("fmt""time" )func B(quit chan<- string) {fmt.Println("B crraied out")quit <- "B" }func A(quit chan<- string, finished chan<- bool) {// 模擬耗時(shí)任務(wù)time.Sleep(time.Second * 1)fmt.Println("A crraied out")finished <- truequit <- "A" }func C(quit chan<- string, finished <-chan bool) {// 在A沒有執(zhí)行完之前,finished獲取不到數(shù)據(jù),會阻塞<-finishedfmt.Println("C crraied out")quit <- "C" }func main() {finished := make(chan bool)defer close(finished)quit := make(chan string)defer close(quit)go A(quit, finished)go B(quit)go C(quit, finished)fmt.Println(<-quit)fmt.Println(<-quit)fmt.Println(<-quit) }正常執(zhí)行我們得到以下結(jié)果
B crraied out B A crraied out A C crraied out C注意:最后從quit中讀數(shù)據(jù)不能使用for-range語法,不然程序會出現(xiàn)死鎖
for res := range quit {fmt.Println(res)} fatal error: all goroutines are asleep - deadlock!原因很簡單,程序中quit通道沒有被close,A、B、C運(yùn)行完了,Go的主協(xié)程在for循環(huán)中阻塞了,所有Go協(xié)程都阻塞了,進(jìn)入了死鎖狀態(tài)
場景五,超時(shí)后停止Go協(xié)程,避免浪費(fèi)資源(停止調(diào)用鏈)
場景四中,假設(shè)A方法掛了或者需要執(zhí)行很長時(shí)間,main協(xié)程會等到所有方法執(zhí)行完才會退出。在實(shí)際應(yīng)用中顯然不行,所以要設(shè)置超時(shí)時(shí)間。問題來了,C方法是基于A方法執(zhí)行完后才執(zhí)行的,我們怎樣通知C方法退出呢。這里針對普通的Go協(xié)程,不是Http請求,有關(guān)Http超時(shí)問題引起的內(nèi)存泄露可以看這篇文章
下面我們修改場景四的代碼,讓A方法有超時(shí)設(shè)置,C方法在A方法超時(shí)后也退出
運(yùn)行結(jié)果
B crraied out B C Exited C timeout A timeout A crraied outA方法用time.Sleep(time.Second * 3)模擬超時(shí)任務(wù),代碼最后讓main協(xié)程休眠,主要為了說明雖然A超時(shí)了,但正常情況下它還是會把任務(wù)執(zhí)行下去的。如果有哪位大俠有什么方法能讓它不執(zhí)行,還請告知!!!
總結(jié)
本文介紹了幾種場景下channel的使用技巧,希望能起到拋磚引玉的作用,各位如有其它技巧,歡迎評論,本文會把你們的技巧收納在其中。感謝!!!
轉(zhuǎn)載于:https://www.cnblogs.com/FireworksEasyCool/p/11587220.html
總結(jié)
以上是生活随笔為你收集整理的Channel使用技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于并行附加特征提取网络的SSD地面小目
- 下一篇: 论文学习20-End-to-end Se