日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Channel使用技巧

發布時間:2024/7/5 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Channel使用技巧 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

Go協程一般使用channel(通道)通信從而協調/同步他們的工作。合理利用Go協程和channel能幫助我們大大提高程序的性能。本文將介紹一些使用channel的場景及技巧

場景一,使用channel返回運算結果

計算斐波那契數列,在學習遞歸時候這是個經典問題。現在我們不用遞歸實現,而是用channel返回計算得出的斐波那契數列。 計算前40個斐波那契數列的值,看下效率

package mainimport ("fmt""time" ) //計算斐波那契數列并寫到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,效率是遞歸實現的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返回的結果十分便利。當channel關閉且沒有數據時,for循環會自動退出,無需主動監測channel是否關閉。close(ch)只針對寫數據到channel起作用,意思是close(ch)后,ch中不能再寫數據,但不影響從ch中讀數據

場景二,使用channel獲取多個并行方法中的一個結果

假設程序從多個復制的數據庫同時讀取。只需要接收首先到達的一個答案,Query 函數獲取數據庫的連接切片并請求。并行請求每一個數據庫并返回收到的第一個響應:

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 }

場景三,響應超時處理

在調用遠程方法的時候,存在超時可能,超時后返回超時提示

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() {//調用遠程方法}()return outCh }

同樣可以擴展到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()超時設置可能引發的內存泄露問題,可以看這篇文章

場景四,多任務并發執行和順序執行

方法A和B同時執行,方法C等待方法A執行完后才能執行,main等待A、B、C執行完才退出

package mainimport ("fmt""time" )func B(quit chan<- string) {fmt.Println("B crraied out")quit <- "B" }func A(quit chan<- string, finished chan<- bool) {// 模擬耗時任務time.Sleep(time.Second * 1)fmt.Println("A crraied out")finished <- truequit <- "A" }func C(quit chan<- string, finished <-chan bool) {// 在A沒有執行完之前,finished獲取不到數據,會阻塞<-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) }

正常執行我們得到以下結果

B crraied out B A crraied out A C crraied out C

注意:最后從quit中讀數據不能使用for-range語法,不然程序會出現死鎖

for res := range quit {fmt.Println(res)} fatal error: all goroutines are asleep - deadlock!

原因很簡單,程序中quit通道沒有被close,A、B、C運行完了,Go的主協程在for循環中阻塞了,所有Go協程都阻塞了,進入了死鎖狀態

場景五,超時后停止Go協程,避免浪費資源(停止調用鏈)

場景四中,假設A方法掛了或者需要執行很長時間,main協程會等到所有方法執行完才會退出。在實際應用中顯然不行,所以要設置超時時間。問題來了,C方法是基于A方法執行完后才執行的,我們怎樣通知C方法退出呢。這里針對普通的Go協程,不是Http請求,有關Http超時問題引起的內存泄露可以看這篇文章
下面我們修改場景四的代碼,讓A方法有超時設置,C方法在A方法超時后也退出

package mainimport ("fmt""time" )// B方法 func B(quit chan<- string) {fmt.Println("B crraied out")quit <- "B" }// A方法,有超時限制 func AWithTimeOut(quit chan<- string, finishedA chan<- bool, timeout time.Duration) {select {case resp := <-A(finishedA):quit <- respcase <-time.After(timeout):quit <- "A timeout"} }// A需要執行的任務 func A(finishedA chan<- bool) <-chan string {respCh := make(chan string)go func() {// 模擬耗時任務// time.Sleep(time.Second * 3)fmt.Println("A crraied out")finishedA <- truerespCh <- "A"}()return respCh }// C方法,等待A方法完成后才能執行,同樣有超時限制,超時時間和A方法一致 func CWithTimeOut(quit chan<- string, finishedA <-chan bool, timeout time.Duration) {select {case <-finishedA:fmt.Println("C crraied out")quit <- "C"case <-time.After(timeout):fmt.Println("C Exited")quit <- "C timeout"} }func main() {finishedA := make(chan bool, 1) //這里必須要是1的緩沖通道,不然超時后會死鎖defer close(finishedA)quit := make(chan string, 3)defer close(quit)timeout := time.Second * 2go AWithTimeOut(quit, finishedA, timeout)go B(quit)go CWithTimeOut(quit, finishedA, timeout)fmt.Println(<-quit)fmt.Println(<-quit)fmt.Println(<-quit)time.Sleep(time.Second * 3) //如果程序未退出的話,A方法執行的任務還會繼續運行,因為我們沒辦法讓A方法停下來 }

運行結果

B crraied out B C Exited C timeout A timeout A crraied out

A方法用time.Sleep(time.Second * 3)模擬超時任務,代碼最后讓main協程休眠,主要為了說明雖然A超時了,但正常情況下它還是會把任務執行下去的。如果有哪位大俠有什么方法能讓它不執行,還請告知!!!

總結

本文介紹了幾種場景下channel的使用技巧,希望能起到拋磚引玉的作用,各位如有其它技巧,歡迎評論,本文會把你們的技巧收納在其中。感謝!!!

轉載于:https://www.cnblogs.com/FireworksEasyCool/p/11587220.html

總結

以上是生活随笔為你收集整理的Channel使用技巧的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。