中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Go語言之通道

發布時間:2020-07-25 14:51:39 來源:網絡 閱讀:424 作者:baby神 欄目:編程語言

上一篇我們講的原子函數和互斥鎖,都可以保證共享數據的讀寫。但是呢,它們還是有點復雜,而且影響性能。對此,Go又為我們提供了一種工具,這就是通道


所以在多個goroutine并發中,我們不僅可以通過原子函數和互斥鎖保證對共享資源的安全訪問,消除競爭的狀態,還可以通過使用通道,在多個goroutine發送和接受共享的數據,達到數據同步的目的。


通道,它有點像在兩個routine之間架設的管道:一個goroutine可以往這個管道里塞數據,另外一個可以從這個管道里取數據。有點類似于我們說的隊列。


聲明一個通道很簡單,我們使用chan關鍵字即可。除此之外,還要指定通道中發送和接收數據的類型,這樣我們才能知道,要發送什么類型的數據給通道,也知道從這個通道里可以接收到什么類型的數據。


ch:=make(chan int)


通道類型和Map這些類型一樣,可以使用內置的make函數聲明初始化。這里我們初始化了一個chan int類型的通道,所以我們只能往這個通道里發送int類型的數據,當然接收也只能是int類型的數據。


我們知道,通道是用于在goroutine之間通信的,它具有發送和接收兩個操作,而且這兩個操作的運算符都是<-


ch <- 2 //發送數值2給這個通道
x:=<-ch //從通道里讀取值,并把讀取的值賦值給x變量
<-ch //從通道里讀取值,然后忽略


看例子,慢慢理解發送和接收的用法。發送操作<-在通道的后面,看箭頭方向,表示把數值 2 發送到通道ch里;接收操作<-在通道的前面,而且是一個一元操作符,看箭頭方向,表示從通道ch里讀取數據。讀取的數據可以賦值給一個變量,也可以忽略。


通道我們還可以使用內置的close函數關閉。


close(ch)


如果一個通道被關閉了,我們就不能往這個通道里發送數據了,如果發送的話,會引起painc異常。但是,我們還可以接收通道里的數據,如果通道里沒有數據的話,接收的數據是nil


剛剛我們使用make函數初始化的時候,只有一個參數,其實make還可以有第二個參數,用于指定通道的大小。默認沒有第二個參數的時候,通道的大小為 0 ,這種通道也被成為無緩沖通道


ch:=make(chan int)
ch:=make(chan int,0)
ch:=make(chan int,2)


看例子,其中第一個和第二個初始化是等價的。第三個初始化創建了一個大小為 2 的通道,這種稱為有緩沖通道。


無緩沖的通道


無緩沖的通道指的是通道的大小為 0 。也就是說,這種類型的通道在接收前沒有能力保存任何值,它要求發送goroutine和接收goroutine同時準備好,才可以完成發送和接收操作。


從上面無緩沖的通道定義來看,發送goroutine和接收gouroutine必須是同步的。同時準備后,如果沒有同時準備好的話,先執行的操作就會阻塞等待,直到另一個相對應的操作準備好為止。這種無緩沖的通道我們也稱之為同步通道


func main() {
    ch := make(chan int)
    go func() {
            var sum int = 0
            for i := 0; i < 10; i++ {
                sum += i            }
            ch <- sum    }()

    fmt.Println(<-ch)}


在前面的例子中,我們為了演示goroutine,防止程序提前終止,都是使用sync.WaitGroup進行等待。現在的這個例子就不用了,我們使用同步通道來等待。


在計算sum和的goroutine沒有執行完,把值賦給ch通道之前,fmt.Println(<-ch)會一直等待,所以main主goroutine就不會終止。只有當計算和的goroutine完成,并且發送到ch通道的操作準備好后,同時<-ch就會接收計算好的值,然后打印出來。


管道


我們在使用Bash的時候,有個管道操作|。它的意思是把上一個操作的輸出,當成下一個操作的輸入,連起來,做一連串的處理操作。


  ~ ls |grep 'D'
Desktop
Documents
Downloads


比如上面這個例子的意思是,先使用ls命令,把當前目錄下的目錄和文件列出來,作為下一個grep命令的輸入,然后通過grep命令,匹配我們需要顯示的目錄和文件,這里匹配以D開頭的文件名或者目錄名。


其實我們使用通道也可以做到管道的效果,我們只需要把一個通道的輸出,當成下一個通道的輸入即可。


func main() {
    one := make(chan int)
    two := make(chan int)
    go func() {
        one<-100
    }()
    go func() {
        v:=<-one
        two<-v    }()

    fmt.Println(<-two)}


這里例子中我們定義兩個通道onetwo,然后按照順序,先把 100 發送給通道one,然后用另外一個goroutine從one接收值,再發送給通道two,最終在主goroutine里等著接收打印two通道里的值。這就類似于一個管道的操作,把通道one的輸出,當成通道two的輸入,類似于接力賽一樣。


有緩沖的通道


有緩沖通道,其實是一個隊列,這個隊列的最大容量就是我們使用make函數創建通道時,通過第二個參數指定的。


ch := make(chan int, 3)


這里創建容量為 3 的、有緩沖的通道。對于有緩沖的通道,向其發送操作就是向隊列的尾部插入元素,接收操作則是從隊列的頭部刪除元素,并返回這個剛剛刪除的元素。


當隊列滿的時候,發送操作會阻塞;當隊列空的時候,接受操作會阻塞。有緩沖的通道,不要求發送和接收操作是同步的,相反可以解耦發送和接收操作。


想知道通道的容量以及里面有幾個元素數據怎么辦?其實和map一樣,使用caplen函數就可以了。


cap(ch)
len(ch)


cap函數返回通道的最大容量,len函數返回現在通道里有幾個元素


func mirroredQuery() string {
    responses := make(chan string, 3)
    go func() { responses <- request("asia.gopl.io") }()
    go func() { responses <- request("europe.gopl.io") }()    
    go func() { responses <- request("americas.gopl.io") }()    
    return <-responses // return the quickest response}func request(hostname string) (response string) { /* ... */ }


這是Go語言圣經里比較有意義的一個例子,例子是想獲取服務端的一個數據,不過這個數據在三個鏡像站點上都存在,這三個鏡像分散在不同的地理位置,而我們的目的又是想最快地獲取數據。


所以這里,我們定義了一個容量為 3 的通道responses,然后同時發起 3 個并發goroutine向這三個鏡像獲取數據,獲取到的數據發送到通道responses中,最后我們使用return <-responses返回獲取到的第一個數據,也就是最快返回的那個鏡像的數據。


單向通道


有時候,我們有一些特殊場景,比如限制一個通道只可以接收,但是不能發送;有時候限制一個通道只能發送,但是不能接收,這種通道我們稱為單向通道


定義單向通道也很簡單,只需要在定義的時候,帶上<-即可。


var send chan<- int //只能發送
var receive <-chan int //只能接收


注意<-操作符的位置,在后面是只能發送,對應發送操作;在前面是只能接收,對應接收操作。


單向通道應用于函數或者方法的參數比較多,比如:


func counter(out chan<- int) {
}


例子這樣的,只能進行發送操作,防止使用接收操作。如果使用了接收操作,在編譯的時候就會報錯的。


使用通道可以很簡單地在goroutine之間共享數據,下一篇會具體介紹一些例子,以便更好地理解并發。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

赣榆县| 科技| 安丘市| 称多县| 新营市| 铜山县| 普陀区| 乐平市| 呼伦贝尔市| 邵武市| 收藏| 龙州县| 松原市| 云安县| 泰顺县| 宣威市| 广丰县| 蓝田县| 凤城市| 登封市| 鄯善县| 怀仁县| 崇明县| 莱西市| 枣阳市| 景洪市| 佛教| 洛南县| 黔东| 桓仁| 桐梓县| 瑞丽市| 渭源县| 雷波县| 平塘县| 西昌市| 华容县| 扶沟县| 尚志市| 雅江县| 铜陵市|