欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Go36-10,11-通道

通道

通道(channel)是Go語言的并發(fā)編程模式中重要的一員。通道類型的值本身就是并發(fā)安全的,這也是Go語言自帶的、唯一一個(gè)可以滿足并發(fā)安全性的類型。

下陸網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。成都創(chuàng)新互聯(lián)公司從2013年創(chuàng)立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司

使用通道

聲明一個(gè)通道類型變量,需要確定該通道類型的元素類型,這決定了可以通過這個(gè)通道傳遞什么類型的數(shù)據(jù)。
初始化通道,需要用到Go語言的內(nèi)建函數(shù)make。make函數(shù)除了必須接收類型字面量作為參數(shù),還可以接收一個(gè)int類型的參數(shù)。第二個(gè)參數(shù)是可選的,表示通道的容量。
通道的容量,指通道最多可以緩存多少個(gè)元素:

  • 當(dāng)容量為0時(shí),未設(shè)置第二個(gè)參數(shù)也是0,通道為非緩沖通道
  • 當(dāng)容量大于0時(shí),通過第二個(gè)參數(shù)指定了容量,通道為緩沖通道

一個(gè)通道相當(dāng)于一個(gè)先進(jìn)先出(FIFO)的隊(duì)列。
通道中的各個(gè)元素值都是嚴(yán)格地按照發(fā)送的順序排列的,先被發(fā)送通道的元素值一定會(huì)先被接收。
元素值的發(fā)送和接收都需要用到操作符<-。可以叫它接送操作符。一個(gè)左尖括號(hào)緊接著一個(gè)減號(hào)形象地代表了元素值的傳輸方向。

package main

import "fmt"

func main() {
    ch2 := make(chan int, 3)
    ch2 <- 1
    ch2 <- 2
    ch2 <- 3
    tmp := <- ch2
    fmt.Println(tmp)
    fmt.Println(<- ch2)
    fmt.Println(<- ch2)
}

通道的特性

通道的基本特性:

  1. 發(fā)送操作之間是互斥的,接收操作之間也是互斥的
  2. 發(fā)送操作和接收操作中對(duì)元素值的處理都是不可分割的
  3. 發(fā)送操作在完全完成之前會(huì)被阻塞,接收操作也是這樣

特性一
在同一時(shí)刻,在運(yùn)行時(shí),系統(tǒng)只會(huì)執(zhí)行對(duì)同一個(gè)通道的任意個(gè)發(fā)送操作中的某一個(gè)。知道這個(gè)元素值被完全的賦值進(jìn)通道后,其他針對(duì)該通道的發(fā)送操作才可能被執(zhí)行。接收操作也是這樣。

元素值的復(fù)制
元素值從外界進(jìn)入通道時(shí)會(huì)被復(fù)制。就是說進(jìn)入通道的并不是那個(gè)元素值,而是它的副本。
元素值從通道進(jìn)入外界是會(huì)被移動(dòng)。這個(gè)移動(dòng)包含2步,先是生成元素值的副本給接收方,然后刪除在通道中的這個(gè)元素值。

特性二
不可分割的意思,如果是發(fā)送操作,就是要復(fù)制元素值,一旦開始執(zhí)行,一定會(huì)復(fù)制完畢。 不會(huì)出現(xiàn)值賦值了一部分的情況。
如果是接收操作,這里有2步,在生成副本后一定會(huì)刪除掉通道中的元素值。不會(huì)出現(xiàn)通道中有殘留原來的副本的情況。
這是為了保證通道中元素值的完整性,也是為了保證通道操作的唯一性。

特性三
發(fā)送操作包括了“復(fù)制元素值”和“放置副本到通道內(nèi)部”這兩個(gè)步驟。在這兩個(gè)步驟完成之前,那句代碼會(huì)一直阻塞在那里。在通道完成發(fā)送操作之后,系統(tǒng)會(huì)通知這句代碼所在的goroutine,使它可以去爭取繼續(xù)運(yùn)行代碼的機(jī)會(huì)。
接收操作包括了“復(fù)制通道內(nèi)的元素值”、“放置副本到接收方”和“刪掉原值”這三個(gè)步驟。完成全部操作執(zhí)行,同樣也是阻塞的。
如此阻塞代碼,其實(shí)就是為了實(shí)現(xiàn)操作的互斥(特性一)和元素值的完整(特性二)。

阻塞的問題

這里分別講 非緩沖通道 和 緩沖通道 的情況。

緩沖通道
如果通道已滿,那么對(duì)它的所有發(fā)送操作都會(huì)被阻塞,直到通道中有元素值被接收走。
對(duì)于有多個(gè)阻塞的發(fā)送操作,會(huì)優(yōu)先通知最早的那個(gè)因?yàn)橥ǖ罎M了而等待的、那個(gè)發(fā)送操作所在的goroutinr,于是會(huì)在收到通知后再次執(zhí)行發(fā)送操作。由于發(fā)送操作在阻塞后,所在的goroutine會(huì)順序進(jìn)入通道內(nèi)部的發(fā)送等待隊(duì)列,所以通知的順序總是公平的。
如果通道已空,那么對(duì)它的所有接受操作都會(huì)被阻塞,知道通道中有新的元素值出現(xiàn)。這里通道內(nèi)部也有個(gè)接受等待隊(duì)列,保證通知執(zhí)行接收操作的順序。

非緩沖通道
情況簡單一些,無論是發(fā)送操作還是接收操作,一開始執(zhí)行就會(huì)被阻塞,直到配對(duì)的操作也開始執(zhí)行。

非緩沖通道是在用同步的方式傳遞數(shù)據(jù)。就是只有收發(fā)雙方對(duì)接上,數(shù)據(jù)才會(huì)被傳送。并且數(shù)據(jù)是直接從發(fā)送方賦值到接收方的,中間不會(huì)用非緩沖通道做中轉(zhuǎn)。
緩沖通道則是在用異步的方式傳遞數(shù)據(jù)。緩沖通道會(huì)作為收發(fā)雙方的中間件,元素值是先從發(fā)送方賦值的緩沖通道,之后再由緩沖通道賦值給接收方。但是,當(dāng)發(fā)送操作在執(zhí)行的時(shí)候發(fā)泄空的通道中,正好有等待的接收操作是,會(huì)直接把元素復(fù)制給接收方。

值為nil的通道
就是未做初始化的通道,對(duì)它的發(fā)送或接收操作都會(huì)永久的處于阻塞狀態(tài)。這是個(gè)錯(cuò)誤使用通道而造成阻塞的情況。所以不要忘記初始化通道。

關(guān)閉通道

上面說了不要忘記初始化通道。對(duì)于已經(jīng)初始化的通道,收發(fā)操作一定不會(huì)引發(fā)Panic。除非通道關(guān)閉,通道一旦關(guān)閉,再對(duì)它接你發(fā)送操作,就會(huì)引發(fā)Panic。
關(guān)閉通道的操作只能執(zhí)行一次,嘗試關(guān)閉一個(gè)已經(jīng)關(guān)閉了的通道,也會(huì)引發(fā)Panic。
接收操作,關(guān)閉通道后,不會(huì)影響接收通道內(nèi)還未取出的值,也就是說是可以繼續(xù)取值的。即使值取完了,接收操作也還能一直取到值,不會(huì)阻塞,此時(shí)取到的是通道內(nèi)類型的零值:

package main

import "fmt"

func main() {
    ch2 := make(chan int, 3)
    ch2 <- 1
    ch2 <- 2
    ch2 <- 3
    fmt.Println(<- ch2)
    close(ch2)  // 關(guān)閉通道不影響取值
    // ch2 <- 4  // 關(guān)閉后不能再發(fā)送值了
    fmt.Println(<- ch2)
    fmt.Println(<- ch2)
    fmt.Println(<- ch2)  // 取完了,就一直返回零值
    fmt.Println(<- ch2)
    fmt.Println(<- ch2)
}

接收操作,是可以感知到通道關(guān)閉的,并能夠安全退出。接收表達(dá)式可以返回兩個(gè)變量。第二個(gè)變量是bool類型,一般用ok命名。如果返回值為false就說明通道已經(jīng)關(guān)閉了,并且再?zèng)]有元素值可取了。注意是并且,也就是如果通道關(guān)閉,但是里面還有未取出的元素,返回的還是true??梢詤⒖家幌氯缦率纠男Ч?/p>

package main

import "fmt"

func main() {
    ch2 := make(chan int, 2)
    // 發(fā)送方
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("發(fā)送方發(fā)送值:", i)
            ch2 <- i
        }
        fmt.Println("發(fā)送完畢,關(guān)閉通道。")
        close(ch2)
    }()
    // 接收方
    for {
        elem, ok := <- ch2
        if !ok{
            fmt.Println("感知到通道已經(jīng)關(guān)閉")
            break
        }
        fmt.Println("接收方接收值:", elem)
    }
    fmt.Println("結(jié)束...")
}

最好不要讓接收方來關(guān)閉通道,而應(yīng)當(dāng)讓發(fā)送方來關(guān)閉。

單向通道

之前說的通道,指的都是雙向通道,就是既能發(fā)也能收。
單向通道,只能發(fā)不能收,或者只能收不能發(fā)的通道。定義單向通道的方法:

var c1 = make(chan<- int, 1)  // 只能發(fā)不能收
var c2 = make(<-chan int, 1)  // 只能收不能發(fā)

只能發(fā)不能收的通道,可以簡稱為發(fā)送通道。
只能收不能發(fā)的通道,可以簡稱為接收通道

單向通道約束函數(shù)行為

通過單向通道可以約束其他代碼的行為。下面是對(duì)之前的一個(gè)例子稍加修改:

package main

import "fmt"

func sender(ch chan<- int) {
    for i := 0; i < 10; i++ {
        fmt.Println("發(fā)送方發(fā)送值:", i)
        ch <- i
    }
    fmt.Println("發(fā)送完畢,關(guān)閉通道...")
    close(ch)
}

func reveiver(ch <-chan int) {
    for {
        elem, ok := <- ch
        if !ok {
            fmt.Println("感知到通道已經(jīng)關(guān)閉")
            break
        }
        fmt.Println("接收方接收值:", elem)
    }
}

func main() {
    intChan := make(chan int, 2)
    // 發(fā)送方
    go sender(intChan)
    // 接收方
    reveiver(intChan)
    fmt.Println("結(jié)束...")
}

這里把之前主函數(shù)里的發(fā)送方和接收方的代碼都封裝了一個(gè)函數(shù)中去了。在函數(shù)內(nèi)部只接收單向通道,這樣在封裝函數(shù)內(nèi)部,就只能對(duì)該通道進(jìn)行定義的單向操作。在調(diào)用函數(shù)的時(shí)候,仍然是把雙向通道傳給它。Go語言會(huì)自動(dòng)把雙向通道轉(zhuǎn)換為函數(shù)所需的單向通道。這樣在函數(shù)內(nèi)部該通道就是單向的,但是在函數(shù)外部就沒有限制。

單向通道約束調(diào)用方

下面的例子中定義了函數(shù)getIntChan,該函數(shù)會(huì)返回一個(gè) <-chan int 類型的通道。得到該通道的程序只能從通道中接收元素。這是對(duì)函數(shù)調(diào)用方的一種約束:

package main

import "fmt"

func getIntChan() <-chan int {
    num := 5
    ch := make(chan int, num)
    for i := 0; i < num; i++ {
        ch <- i
    }
    close(ch)
    return ch
}

func main() {
    intChan := getIntChan()
    for elem := range intChan {
        fmt.Println(elem)
    }
}

上面用了for range來遍歷通道中的所有元素。

select語句

我們可以使用帶range子句的for語句從通道中獲取數(shù)據(jù),也可以通過select語句操縱通道。

示例

select語句只能與通道聯(lián)用,它一般由若干個(gè)分支組成。由于select語句是專為通道而設(shè)計(jì)的,所以每個(gè)case表達(dá)式中都只能包含操作通道的表達(dá)式,比如接收表達(dá)式。如果還需要把接收表達(dá)式賦值給變量的話,可以寫成賦值語句或短變量聲明:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func init() {
    rand.Seed(time.Now().UnixNano())
}

func main() {
    // 生成了包括3個(gè)通道的數(shù)組
    intChannels := [3]chan int{
        make(chan int, 1),
        make(chan int, 1),
        make(chan int, 1),
    }
    // 生成返范圍在[0,2]的隨機(jī)數(shù),效果就是隨機(jī)選擇一個(gè)通道
    index := rand.Intn(3)
    fmt.Println("index:", index)
    intChannels[index] <- index
    // 哪個(gè)通道中遠(yuǎn)元素可以取出,那個(gè)對(duì)應(yīng)的分支就會(huì)被執(zhí)行
    select {
    case <-intChannels[0]:
        fmt.Println("選中了第一個(gè)通道")
    case <-intChannels[1]:
        fmt.Println("選中了第二個(gè)通道")
    case elem := <- intChannels[2]:
        fmt.Println("選中了第三個(gè)通道,值:", elem)
    default:  // 這個(gè)默認(rèn)分支不會(huì)被選中
        fmt.Println("沒有選中任何通道")
    }
}

使用select語句的注意點(diǎn)

使用select語句,需要注意一下幾點(diǎn):

  1. 如果加入了默認(rèn)分支,select語句就不會(huì)被阻塞。如果所有通道的表達(dá)式都阻塞了,那么就會(huì)執(zhí)行默認(rèn)分支
  2. 如果沒有加入默認(rèn)分支,那一旦所有的表達(dá)式都沒有滿足求職條件,select就會(huì)阻塞。知道至少有一個(gè)case表達(dá)式滿足條件
  3. 可能會(huì)因?yàn)橥ǖ狸P(guān)閉了,而直接從通道接到到一個(gè)元素類型的零值。這時(shí)候就需要通過表達(dá)式的第二個(gè)返回值來判斷通道是否關(guān)閉,一旦發(fā)現(xiàn)某個(gè)通道關(guān)閉了,就應(yīng)該及時(shí)屏蔽掉對(duì)應(yīng)的分支或者采取別的措施。
  4. select語句只能對(duì)其中的每一個(gè)case表達(dá)式各求值一次。如果想連續(xù)的或是定時(shí)的操作其中的通道,需要通過for循環(huán)嵌入select來實(shí)現(xiàn)。這樣的話要主要,簡單的在select里使用break,只能結(jié)束當(dāng)前的select,不會(huì)退出for循環(huán)。

下面是通過表達(dá)式的第二個(gè)返回值判斷通道關(guān)閉的示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    intChan := make(chan int, 1)
    // 3秒后關(guān)閉通道
    time.AfterFunc(time.Second * 3, func() {
        close(intChan)
    })
    select {
    case _, ok := <- intChan:
        if !ok {
            fmt.Println("通道已經(jīng)關(guān)閉")
            break
        }
        fmt.Println("通道里有值傳入")
    }
}

select語句的分支選擇的規(guī)則

規(guī)則如下所示:

  1. 當(dāng)case表達(dá)式被求值是,如果包含多個(gè)表達(dá)式,總會(huì)按照從左到有的順序被求職
  2. 所有case表達(dá)式都會(huì)被求職,并且是按從上到下的順序。結(jié)合上面一條,就是先從左到右,再從上到下對(duì)所有的表達(dá)式求職
  3. 如果是發(fā)送表達(dá)式或者接受表達(dá)式,在被求值時(shí)處于阻塞狀態(tài)就認(rèn)為是求職不成功,也就是case表達(dá)式所在的分支不滿足選擇條件
  4. 僅當(dāng)select語句里所有的case表達(dá)式都被求職完畢后,才會(huì)開始選擇候選分支。如果所有分支都不滿足,那就選默認(rèn)分支。如果沒有默認(rèn)分支,就阻塞,直到至少有一個(gè)候選分支返回條件為止。
  5. 如果有多個(gè)滿足條件的分支,那么會(huì)有一種偽隨機(jī)算法選擇其中一個(gè)分支并執(zhí)行。
  6. 一條select語句只能由一個(gè)默認(rèn)分支。默認(rèn)分支只在無候選分支是才會(huì)被執(zhí)行,與它的編寫位置無關(guān)。
  7. select語句的每次執(zhí)行,包括case表達(dá)式求值和分支選擇,都是獨(dú)立的。但是,不是并發(fā)安全的,具體要看其中的代碼是否是并發(fā)安全了。

下面是驗(yàn)證上述規(guī)則的示例:

package main

import "fmt"

var channels = [3]chan int{
    nil,
    make(chan int),
    nil,
}

var numbers = []int{1, 2, 3}

func main() {
    select {
    case getChan(0) <- getNumber(0):
        fmt.Println("The first candidate case is selected.")
    case getChan(1) <- getNumber(1):
        fmt.Println("The second candidate case is selected.")
    case getChan(2) <- getNumber(2):
        fmt.Println("The third candidate case is selected")
    default:
        fmt.Println("No candidate case is selected!")
    }
}

func getNumber(i int) int {
    fmt.Printf("numbers[%d]\n", i)
    return numbers[i]
}

func getChan(i int) chan int {
    fmt.Printf("channels[%d]\n", i)
    return channels[i]
}
/* 執(zhí)行結(jié)果
PS H:\Go\src\Go36\article11\example05> go run main.go
channels[0]
numbers[0]
channels[1]
numbers[1]
channels[2]
numbers[2]
No candidate case is selected!
PS H:\Go\src\Go36\article11\example05>
*/

本文標(biāo)題:Go36-10,11-通道
網(wǎng)頁鏈接:http://chinadenli.net/article14/jgijge.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、建站公司、手機(jī)網(wǎng)站建設(shè)、網(wǎng)站維護(hù)網(wǎng)站建設(shè)、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)
日韩人妻一区中文字幕| 国产级别精品一区二区视频| 国产精品不卡免费视频| 亚洲精品欧美精品一区三区| 国产二级一级内射视频播放| 日韩精品视频香蕉视频| 在线播放欧美精品一区| 成人欧美一区二区三区视频| 国产一区二区三中文字幕| 亚洲欧美日韩另类第一页| 国产乱久久亚洲国产精品| 老鸭窝老鸭窝一区二区| 亚洲中文字幕在线综合视频| 黑人巨大精品欧美一区二区区 | 日韩1区二区三区麻豆| 国产一区二区三区免费福利| 视频在线免费观看你懂的| 中文字幕亚洲精品人妻| 欧美日韩精品一区二区三区不卡| 国产亚洲精品香蕉视频播放| 国产午夜福利在线观看精品| 婷婷色香五月综合激激情| 亚洲国产性感美女视频| 九九九热在线免费视频| 欧美日韩综合在线第一页| 黄色三级日本在线观看| 国产日韩综合一区在线观看| 精品欧美日韩一区二区三区| 成年人视频日本大香蕉久久| 日韩欧美一区二区久久婷婷| 伊人久久五月天综合网| 日本高清二区视频久二区| 国产中文字幕一二三区| 中国美女偷拍福利视频| 91人妻人人揉人人澡人| 久久国产精品熟女一区二区三区| 久久国产精品热爱视频| 欧美日韩国产综合在线| 国产精品美女午夜福利| 久久综合亚洲精品蜜桃| 国产一级性生活录像片|