Go channel

單純地將函數(shù)并發(fā)執(zhí)行是沒(méi)有意義的峦朗,函數(shù)與函數(shù)之間需要交換數(shù)據(jù)才能體現(xiàn)并發(fā)執(zhí)行函數(shù)的作用淮逻。雖然可使用共享內(nèi)存進(jìn)行數(shù)據(jù)交換塞俱,但共享內(nèi)存在不同的Goroutine中容易發(fā)生竟態(tài)問(wèn)題飞涂。為了保證數(shù)據(jù)交換的正確性恩沛,必須使用互斥量對(duì)內(nèi)存進(jìn)行加鎖枣耀,但加鎖會(huì)變成串行勢(shì)必造成性能問(wèn)題霉晕。

Golang的并發(fā)模型是CSP(Communicating Sequential Processes),提倡通過(guò)通信共享內(nèi)存而非通過(guò)共享內(nèi)存實(shí)現(xiàn)通信捞奕。

Golang提倡使用通信的方式來(lái)代替共享內(nèi)存牺堰,當(dāng)一個(gè)資源需要在不同的Goroutine之間共享時(shí),Channel會(huì)在Goroutine之間架起一個(gè)管道颅围,以提供確保同步交換數(shù)據(jù)的機(jī)制伟葫。Goroutine是Golang程序并發(fā)的執(zhí)行體,Channel是Goroutine之間的通信機(jī)制院促。每個(gè)Channel都是一個(gè)通信機(jī)制筏养,可以讓一個(gè)Goroutine通過(guò)它給另外一個(gè)Goroutine發(fā)送值數(shù)據(jù)。Channel提供了一種機(jī)制在兩個(gè)并發(fā)執(zhí)行的函數(shù)之間進(jìn)行同步常拓,通過(guò)傳遞與該Channel元素類(lèi)型相符的值來(lái)進(jìn)行通信渐溶。

  • Goroutine運(yùn)行在相同的地址空間,因此訪(fǎng)問(wèn)共享內(nèi)存必須做好同步弄抬。
  • Channel是線(xiàn)程安全的茎辐,多個(gè)Goroutine訪(fǎng)問(wèn)時(shí)無(wú)需加鎖。

隊(duì)列

由于多個(gè)Goroutine為了爭(zhēng)搶數(shù)據(jù)勢(shì)必造成執(zhí)行的低效率,通道使用了一種類(lèi)似隊(duì)列的結(jié)構(gòu)荔茬,通過(guò)隊(duì)列以提高執(zhí)行的效率废膘。Golang中Channel是一種特殊的數(shù)據(jù)類(lèi)型,類(lèi)似一個(gè)傳送帶或隊(duì)列(本質(zhì)上是一個(gè)數(shù)據(jù)結(jié)構(gòu)-隊(duì)列)慕蔚,數(shù)據(jù)遵循先進(jìn)先出(FIFO, First In First Out)規(guī)則以保證收發(fā)數(shù)據(jù)順序丐黄。

goroutine和channel的通信

通道聲明

channel是Golang一種特殊的數(shù)據(jù)類(lèi)型,類(lèi)似UNIX系統(tǒng)中的管道或消息隊(duì)列孔飒,通過(guò)Goroutine可發(fā)送或接收數(shù)據(jù)進(jìn)行通訊灌闺。聲明channel時(shí),需指定將要被共享數(shù)據(jù)的類(lèi)型坏瞄,channel自身必須關(guān)聯(lián)一個(gè)數(shù)據(jù)類(lèi)型(需要一個(gè)類(lèi)型進(jìn)行修飾)桂对,即channel可以發(fā)送數(shù)據(jù)的類(lèi)型。

var 變量 chan 類(lèi)型

channel類(lèi)型也就是通道內(nèi)元素傳輸?shù)臄?shù)據(jù)類(lèi)型鸠匀,通道類(lèi)型的空值是nil蕉斜。

var ch chan int
fmt.Printf("value = %v, type = %T, address = %p\n", ch, ch, &ch)
value = <nil>, type = chan int, address = 0xc000006028

通道初始化

channelmap類(lèi)型類(lèi)似,channel擁有一個(gè)使用make()創(chuàng)建的底層數(shù)據(jù)結(jié)構(gòu)的引用(分配內(nèi)存)缀棍,因此 channel是引用類(lèi)型宅此,聲明后需要使用make初始化后才能寫(xiě)入數(shù)據(jù),同時(shí)向channel中寫(xiě)入數(shù)據(jù)時(shí)不能超過(guò)其容量爬范。

實(shí)例 := make(chan 元素類(lèi)型, [緩沖大小])

例如:聲明int類(lèi)型的channel跟伏,只能保存int類(lèi)型的數(shù)據(jù)役耕,也就是說(shuō)一端只能向此channel中放入int類(lèi)型的數(shù)據(jù)套像,另一端只能從此channel中讀取int類(lèi)型的值景馁。

ch := make(chan int, 100)

channel實(shí)例是通過(guò)make創(chuàng)建的句柄,元素類(lèi)型則表示Channel內(nèi)傳輸?shù)臄?shù)據(jù)類(lèi)型斥难,當(dāng)作為參數(shù)或返回值時(shí)需指定為類(lèi)似ch chan int的格式枝嘶。

例如:聲明channel并分配內(nèi)存

var ch chan int
ch = make(chan int, 10)
fmt.Printf("value = %v, type = %T, address = %p\n", ch, ch, &ch)
value = 0xc00007e000, type = chan int, address = 0xc000006028

例如:查看channel長(zhǎng)度和容量

var ch chan int
ch = make(chan int, 10)
fmt.Printf("value = %v, type = %T, address = %p, len = %d, cap = %d\n", ch, ch, &ch, len(ch), cap(ch))
value = 0xc00007e000, type = chan int, address = 0xc000006028, len = 0, cap = 10

通道緩存

使用make()函數(shù)初始化channel時(shí)可以設(shè)置容量(capacity),容量表示通道容納的最多的元素?cái)?shù)量蘸炸,即channel的緩存大小躬络。

ch := make(chan datatype, capacity)

根據(jù)創(chuàng)建channel時(shí)是否設(shè)置容量可將其分為兩種類(lèi)型,分別是unbuffered channelbuffered channel搭儒。

  • 若沒(méi)有設(shè)置容量或容量為0說(shuō)明channel沒(méi)有緩存穷当,發(fā)送方和接收方都準(zhǔn)備完畢后通訊會(huì)發(fā)生阻塞(blocking)。
  • 若設(shè)置了緩存即可能不會(huì)發(fā)生阻塞淹禾,只有緩存滿(mǎn)了之后發(fā)送時(shí)才會(huì)阻塞馁菜,只有緩存空了后接收方才會(huì)阻塞。而一個(gè)nil的通道是不會(huì)通信的铃岔。
容量 緩沖 阻塞
capacity = 0 無(wú)緩沖 阻塞讀寫(xiě)
capacity > 0 有緩沖 非阻塞汪疮,直到寫(xiě)滿(mǎn)capacity個(gè)元素后才會(huì)阻塞寫(xiě)入峭火。
  • 聲明channel時(shí)make(chan Type)若沒(méi)有指定容量則相當(dāng)于make(chan Type, 0)
ch1 := make(chan int)
fmt.Printf("ch1: cap=%d\n", cap(ch1))//ch1: cap=0

ch2 := make(chan int, 0)
fmt.Printf("ch2: cap=%d\n", cap(ch2))//ch2: cap=0

通道操作

對(duì)通道的發(fā)送和接收操作都會(huì)在編譯期間轉(zhuǎn)換為底層的發(fā)送接收函數(shù)

通道操作 示例 描述
發(fā)送 ch<-10 將一個(gè)值發(fā)送到通道中
接受 <-ch 從一個(gè)通道中接收值智嚷,若接受后未賦值則會(huì)忽略結(jié)果卖丸。
關(guān)閉 close(ch) 關(guān)閉通道
示例 描述
channel <- value 發(fā)送value到channel
<-channel 接收并將其丟棄
val := <- channel 從通道中接收數(shù)據(jù)并賦值給val
val, ok := <- channel 從通道中接收數(shù)據(jù)并賦值給val,同時(shí)檢查通道是否已關(guān)閉或是否為空盏道。

關(guān)閉通道

通道狀態(tài) 描述
nil 未初始化的狀態(tài)稍浆,只進(jìn)行了聲明或手動(dòng)賦值為nil
active 正常通道猜嘱,可讀或可寫(xiě)衅枫。
closed 已關(guān)閉,關(guān)閉后通道的值并非為nil朗伶。

關(guān)閉通道需調(diào)用Golang內(nèi)置的close()函數(shù)來(lái)實(shí)現(xiàn)弦撩,需要注意的是,只有在通知接收方Goroutine所有數(shù)據(jù)都發(fā)送完畢時(shí)才需要關(guān)閉通道论皆。通道可以被垃圾回收機(jī)制回收益楼。和關(guān)閉文件不同的是,在結(jié)束操作之后關(guān)閉文件是必須的纯丸,但關(guān)閉通道卻不是偏形。

close(ch)

關(guān)閉通道操作原則上應(yīng)該由發(fā)送方完成静袖,若仍然向一個(gè)已關(guān)閉的通道發(fā)送數(shù)據(jù)則會(huì)導(dǎo)致程序拋出panic觉鼻。若由接收者關(guān)閉通道也會(huì)觸發(fā)panic風(fēng)險(xiǎn)。

package main

func main() {
    c := make(chan int, 2)
    close(c)
    c <- 1// panic: send on closed channel
}

從一個(gè)已經(jīng)關(guān)閉的通道中讀取數(shù)據(jù)時(shí)需要注意的是队橙,接收者不會(huì)被一個(gè)已經(jīng)關(guān)閉的通道阻塞坠陈。接收者從關(guān)閉的通道中仍然可以讀取數(shù)據(jù),不過(guò)此時(shí)是通道的數(shù)據(jù)據(jù)類(lèi)型的默認(rèn)值捐康。此時(shí)可判斷讀取狀態(tài)仇矾,若為false則表示通道已經(jīng)被關(guān)閉。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 2)
    go func() {
        c <- 1
        time.Sleep(time.Second)

        c <- 2
        time.Sleep(time.Second)

        close(c)
    }()
    for i := 0; i < 4; i++ {
        val, ok := <-c
        fmt.Printf("receive %v status %t\n", val, ok)
    }
}
receive 1 status true
receive 2 status true
receive 0 status false
receive 0 status false

上例中工作goroutine關(guān)閉通道前解总,主goroutine仍然會(huì)被工作goroutine所阻塞贮匕,因此讀取數(shù)據(jù)時(shí),注意狀態(tài)位花枫。當(dāng)工作goroutine關(guān)閉通道之后刻盐,主goroutine仍然可以從通道中讀取int類(lèi)型的默認(rèn)值0,只不過(guò)此時(shí)狀態(tài)變量會(huì)變?yōu)?code>false劳翰,而且不再被阻塞敦锌,直到循環(huán)結(jié)束。

通道方向

ChannelType = ("chan" | "chan" "<-" | "<-" "chan") ElementType .
方向 示例
單向 (chan<- | <-chan) ElementType
雙向 chan ElementType

channel類(lèi)型包括三種類(lèi)型的定義佳簸,可選的<-代表channel的方向乙墙,<-優(yōu)先和最左側(cè)類(lèi)型結(jié)合。如果沒(méi)有指定方向,那么channel就是雙向的听想,即可以接收數(shù)據(jù)腥刹,也可以發(fā)送數(shù)據(jù)。

chan T //可以接收和發(fā)送數(shù)據(jù)類(lèi)型為T(mén)的數(shù)據(jù)
chan<- float64 //僅用于發(fā)送float64類(lèi)型的數(shù)據(jù)
<-chan int //僅用于接收int類(lèi)型的數(shù)據(jù)

單向通道

所謂的單向通道是對(duì)通道的一種讀寫(xiě)使用限制汉买。單向通道只能寫(xiě)入或讀取數(shù)據(jù)肛走,由于通道本身是同時(shí)支持讀寫(xiě)的。

操作 示例 描述
只寫(xiě) var 實(shí)例 chan<- 元素類(lèi)型 聲明只能寫(xiě)入數(shù)據(jù)的通道
只讀 var 實(shí)例 <-chan 元素類(lèi)型 聲明只能讀取數(shù)據(jù)的通道

例如:聲明channel录别,設(shè)置只能單向?qū)懭搿?/p>

ch := make(chan int)
var senderChannel chan<- int = ch

例如:聲明只能單向?qū)懭氲?code>channel

ch := make(<-chan int)

同步阻塞

  • 同步模式(阻塞):無(wú)緩沖的通道 unbuffered channel

無(wú)緩沖的通道只有當(dāng)發(fā)送方和接收方都準(zhǔn)備好時(shí)才會(huì)傳送數(shù)據(jù)朽色,發(fā)送方和接收方要同步就緒,只有在二者都ready的情況下组题,數(shù)據(jù)才能在兩者之間傳輸(實(shí)際上就是內(nèi)存拷貝)葫男。否則任意一方先行發(fā)送或接收操作都會(huì)被掛起,等待另一方的出現(xiàn)才能被喚醒崔列。

例如:無(wú)緩沖通道發(fā)送時(shí)引發(fā)死鎖

var ch chan int
ch = make(chan int) //聲明無(wú)緩沖的通道梢褐,讀寫(xiě)阻塞。
ch <- 1             //發(fā)生阻塞
fatal error: all goroutines are asleep - deadlock!

例如:無(wú)緩沖通道讀取時(shí)引發(fā)死鎖

var ch chan int
ch = make(chan int) //聲明無(wú)緩沖的通道赵讯,讀寫(xiě)阻塞盈咳。
<-ch                //發(fā)生阻塞
fatal error: all goroutines are asleep - deadlock!

例如:發(fā)送方和接收方要同步就緒,只有在二者都ready的情況下边翼,數(shù)據(jù)才能在兩者之間傳輸(實(shí)際上就是內(nèi)存拷貝)鱼响。

var ch chan int
ch = make(chan int) //聲明無(wú)緩沖的通道,讀寫(xiě)阻塞组底。
ch <- 1             //發(fā)生阻塞
go func() {
    <-ch
}()
fatal error: all goroutines are asleep - deadlock!

例如:無(wú)緩沖通道只有當(dāng)發(fā)送方和接收方都準(zhǔn)備好時(shí)才會(huì)傳送數(shù)據(jù)

var ch chan int
ch = make(chan int) //聲明無(wú)緩沖的通道丈积,讀寫(xiě)阻塞。
go func() {
    <-ch
}()
ch <- 1 //不會(huì)阻塞

例如:為保證執(zhí)行順序添加sync.WaitGroup

var wg sync.WaitGroup

func main() {
    var ch chan int
    ch = make(chan int) //聲明無(wú)緩沖的通道债鸡,讀寫(xiě)阻塞江滨。

    wg.Add(1)
    go func() {
        defer wg.Done()
        v := <-ch
        fmt.Printf("recieve:%d\n", v)
    }()

    v := 1
    ch <- v //不會(huì)阻塞
    fmt.Printf("send:%d\n", v)
    wg.Wait()
}

由于無(wú)緩沖這種阻塞發(fā)送方和接收方的特性,使用時(shí)需要防止死鎖的發(fā)生厌均。如果在一個(gè)Goroutine內(nèi)向同一個(gè)Channel同時(shí)進(jìn)行讀取和發(fā)送則會(huì)導(dǎo)致死鎖唬滑。

例如:

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int)
    go func() {
        fmt.Println("work:ready to send")

        c <- 1
        fmt.Println("work:send 1 to channel")

        fmt.Println("work:start sleep 1 second")
        time.Sleep(time.Second)
        fmt.Println("work:end sleep 1 second")

        c <- 2
        fmt.Println("work:send 2 to channel")
    }()

    fmt.Println("main:start sleep 1 second")
    time.Sleep(time.Second)
    fmt.Println("main:end sleep 1 second")

    val := <-c
    fmt.Println("main:receive value ", val)

    val = <-c
    fmt.Println("main:receive value ", val)

    time.Sleep(time.Second)
}
main:start sleep 1 second
work:ready to send
main:end sleep 1 second
main:receive value  1
work:send 1 to channel
work:start sleep 1 second
work:end sleep 1 second
work:send 2 to channel
main:receive value  2

聲明無(wú)緩沖通道后開(kāi)啟goroutine向通道發(fā)送數(shù)據(jù),然后主線(xiàn)程從通道中讀取數(shù)據(jù)棺弊。主線(xiàn)程休眠期間晶密,goroutine阻塞在發(fā)送向通道發(fā)送數(shù)據(jù)的位置,只有當(dāng)主線(xiàn)程休眠結(jié)束開(kāi)始從通道中讀取數(shù)據(jù)時(shí)镊屎,goroutine才開(kāi)始向下運(yùn)行惹挟。同時(shí),當(dāng)協(xié)程發(fā)送完第一個(gè)數(shù)據(jù)休眠時(shí)缝驳,主線(xiàn)程讀取了第一個(gè)數(shù)據(jù)连锯,準(zhǔn)備從通道中讀取第二個(gè)數(shù)據(jù)時(shí)會(huì)被阻塞归苍,直到協(xié)程休眠結(jié)束向通道發(fā)送數(shù)據(jù)后才會(huì)繼續(xù)運(yùn)行。

從無(wú)緩存的通道中讀取消息時(shí)會(huì)阻塞运怖,直到有goroutine向該通道發(fā)送消息拼弃。同理,向無(wú)緩存的通道中發(fā)送消息時(shí)也會(huì)阻塞摇展,直到有goroutine從通道中讀取消息吻氧。通過(guò)無(wú)緩存的通道進(jìn)行通信時(shí),接收者接收到的數(shù)據(jù)會(huì)發(fā)生在發(fā)送者喚醒之前咏连。

例如:

package main

import (
    "fmt"
    "time"
)

func send(ch chan int) {
    ch <- 1
    ch <- 2
    ch <- 3
}
func receive(ch chan int) {
    var recv int
    for {
        recv = <-ch
        fmt.Println(recv)
    }
}
func main() {
    ch := make(chan int)
    go send(ch)
    go receive(ch)
    time.Sleep(time.Second * time.Duration(2))
}

主函數(shù)中開(kāi)啟兩個(gè)goroutine盯孙,一個(gè)用于執(zhí)行send()函數(shù)用于每次向channel中發(fā)送寫(xiě)入一個(gè)int類(lèi)型的數(shù)值,一個(gè)receive()函數(shù)用于每次從通道中讀取一個(gè)int類(lèi)型的數(shù)值祟滴。當(dāng)channel中沒(méi)有數(shù)據(jù)可讀時(shí)振惰,receivegoroutine會(huì)進(jìn)入阻塞狀態(tài),因?yàn)?code>receive中使用了for無(wú)限循環(huán)垄懂,也就時(shí)說(shuō)receivegoroutine會(huì)一致阻塞下去骑晶,直到從channel中讀取到數(shù)據(jù)。讀取到數(shù)據(jù)后又會(huì)進(jìn)入下一輪循環(huán)草慧,由被阻塞在recv = <-ch上桶蛔。當(dāng)main函數(shù)中的休眠時(shí)間到了指定時(shí)間后,main程序會(huì)終止也就意味著主程序結(jié)束漫谷,此時(shí)所有的goroutine都會(huì)停止執(zhí)行仔雷。

1
2
3

異步非阻塞

  • 異步模式(非阻塞):有緩沖的通道 buffered channel

異步模式下,在緩沖槽可用的情況下抖剿,也就是擁有剩余容量的情況下朽寞,發(fā)送和接收操作都可以順序進(jìn)行。否則操作方同樣會(huì)被掛起斩郎,直到出現(xiàn)相反操作時(shí)才會(huì)被喚醒。

異步模式下喻频,緩沖槽要有剩余容量操作才會(huì)成功缩宜,否則也會(huì)被阻塞。

通多緩存的使用可以盡量避免堵塞甥温,以提供應(yīng)用的性能锻煌。

有緩存的通道類(lèi)似一個(gè)阻塞隊(duì)列(采用環(huán)形數(shù)組實(shí)現(xiàn)),當(dāng)緩存未滿(mǎn)時(shí)向通道中發(fā)送消息不會(huì)堵塞姻蚓,當(dāng)緩存滿(mǎn)時(shí)宋梧,發(fā)送操作將被阻塞,直到有其它goroutine從中讀取消息狰挡。相應(yīng)的捂龄,當(dāng)通道中消息不為空時(shí)释涛,讀取消息不會(huì)出現(xiàn)堵塞,當(dāng)通道為空時(shí)倦沧,讀取操作會(huì)造成阻塞唇撬,直到有goroutine向通道中寫(xiě)入消息。

有緩存的通道區(qū)別在于只有當(dāng)緩沖區(qū)被填滿(mǎn)時(shí)才會(huì)阻塞發(fā)送者展融,只有當(dāng)緩沖區(qū)為空時(shí)才會(huì)阻塞接收者窖认。

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 2)
    go func() {
        for i := 0; i < 4; i++ {
            c <- i
            fmt.Println("work:send ", i)
        }
        time.Sleep(time.Second * 5)
        for i := 4; i < 6; i++ {
            c <- i
            fmt.Println("work:send ", i)
        }
    }()
    for i := 0; i < 6; i++ {
        time.Sleep(time.Second)
        fmt.Println("main:receive ", <-c)
    }
}
work:send  0
work:send  1
main:receive  0
work:send  2
main:receive  1
work:send  3
main:receive  2
main:receive  3
work:send  4
work:send  5
main:receive  4
main:receive  5

聲明容量為2帶緩沖的通道,開(kāi)啟一個(gè)協(xié)程告希,這個(gè)協(xié)程會(huì)向通道連續(xù)發(fā)送4個(gè)數(shù)據(jù)后然后休眠5秒扑浸,然后再向通道發(fā)送2個(gè)數(shù)據(jù)。而主線(xiàn)程則會(huì)從這個(gè)通道中讀取數(shù)據(jù)燕偶,每次讀取前會(huì)先休眠1秒首装。goroutine首先向通道發(fā)送了兩個(gè)數(shù)據(jù)分別為0和1后被阻塞,因?yàn)榇藭r(shí)主線(xiàn)程在運(yùn)行1秒的休眠杭跪。主線(xiàn)程休眠結(jié)束后仙逻,從通道中讀取了第一個(gè)數(shù)據(jù)0后繼續(xù)休眠1秒。通道此時(shí)又有了緩沖涧尿,于是goroutine又向通道發(fā)送了第三個(gè)數(shù)據(jù)2系奉,而后再次因?yàn)橥ǖ赖木彌_區(qū)已滿(mǎn)則進(jìn)入休眠。以此類(lèi)推姑廉,直到協(xié)程將4個(gè)數(shù)據(jù)發(fā)送完畢后缺亮,才開(kāi)始運(yùn)行5秒的休眠。而當(dāng)主線(xiàn)程從通道讀取完第4個(gè)數(shù)據(jù)也就是3之后桥言,當(dāng)準(zhǔn)備再?gòu)耐ǖ乐凶x取第五個(gè)數(shù)據(jù)時(shí)萌踱,由于通道為空,主線(xiàn)程作為接收者被阻塞号阿。直到goroutine的5秒休眠結(jié)束并鸵,再次向通道中發(fā)送數(shù)據(jù)后,主線(xiàn)程讀取到數(shù)據(jù)而不被阻塞扔涧。

應(yīng)用

超時(shí)處理

channel配合select可實(shí)現(xiàn)多路復(fù)用园担,select寫(xiě)法類(lèi)似switch不同之處在于select的每個(gè)case代表一個(gè)通信操作,即在某個(gè)信道上進(jìn)行發(fā)送或接收的操作枯夜,同時(shí)會(huì)包含一些語(yǔ)句組成一個(gè) 語(yǔ)句塊弯汰。

select用于多個(gè)信道監(jiān)聽(tīng)并收發(fā)消息,當(dāng)任何一個(gè)條件滿(mǎn)足時(shí)會(huì)執(zhí)行湖雹,若沒(méi)有可執(zhí)行的case則會(huì)執(zhí)行默認(rèn)的case咏闪。若不存在默認(rèn)的case則程序發(fā)生堵塞。

select默認(rèn)是堵塞的摔吏,只有監(jiān)聽(tīng)的信道中有發(fā)送或接收的數(shù)據(jù)時(shí)才會(huì)運(yùn)行鸽嫂。

生產(chǎn)者消費(fèi)者

生產(chǎn)者消費(fèi)者有一個(gè)著名的線(xiàn)程同步問(wèn)題纵装,即生產(chǎn)者產(chǎn)出后將產(chǎn)品交給若干消費(fèi)者,為使生產(chǎn)者和消費(fèi)者并發(fā)執(zhí)行溪胶,兩者之間會(huì)設(shè)置一個(gè)具有多個(gè)緩沖區(qū)的緩沖池搂擦,生產(chǎn)者將產(chǎn)出產(chǎn)品放入緩沖池,消費(fèi)者從緩沖池取出產(chǎn)品哗脖,此時(shí)生產(chǎn)者和消費(fèi)者之間必須保持同步瀑踢,即不允許消費(fèi)者到一個(gè)空緩沖區(qū)內(nèi)獲取產(chǎn)品,也不允許生產(chǎn)者向一個(gè)已經(jīng)存放產(chǎn)品的緩沖區(qū)中再次投放產(chǎn)品才避。

Go語(yǔ)言的channel信道天生具有這種特性橱夭,即當(dāng)緩沖區(qū)滿(mǎn)時(shí)寫(xiě)空時(shí)讀都會(huì)被阻塞,另外channel本身就是并發(fā)安全的桑逝。

使用單向信道創(chuàng)建生產(chǎn)者消費(fèi)者模式

package main

import "fmt"

func producer(out chan<- int) {
    for i := 0; i < 10; i++ {
        out <- i * i
    }
    close(out)
}
func consumer(in <-chan int) {
    for num := range in {
        fmt.Println("num = ", num)
    }
}

func main() {
    ch := make(chan int) //創(chuàng)建雙向信道
    go producer(ch)      //創(chuàng)建并發(fā)執(zhí)行單元作為生產(chǎn)者 生產(chǎn)數(shù)字寫(xiě)入信道
    consumer(ch)         //消費(fèi)者 從信道中讀取數(shù)據(jù) 打印輸出
}
num =  0
num =  1
num =  4
num =  9
num =  16
num =  25
num =  36
num =  49
num =  64
num =  81

生產(chǎn)者消費(fèi)者模式

package main

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

func producer(out chan<- string) {
    for {
        out <- fmt.Sprintf("%v", rand.Float64())
        time.Sleep(time.Second * time.Duration(1))
    }
}
func consumer(in <-chan string) {
    for {
        msg, ok := <-in //若信道無(wú)數(shù)據(jù)則發(fā)生堵塞
        if !ok {
            fmt.Println("channel close")
            break
        }
        fmt.Println("msg = ", msg)
    }
}

func main() {
    ch := make(chan string, 5) //創(chuàng)建雙向信道
    go producer(ch)            //創(chuàng)建并發(fā)執(zhí)行單元作為生產(chǎn)者 生產(chǎn)數(shù)字寫(xiě)入信道
    consumer(ch)               //消費(fèi)者 從信道中讀取數(shù)據(jù) 打印輸出
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棘劣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子楞遏,更是在濱河造成了極大的恐慌茬暇,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寡喝,死亡現(xiàn)場(chǎng)離奇詭異糙俗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)预鬓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)巧骚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人格二,你說(shuō)我怎么就攤上這事劈彪。” “怎么了顶猜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沧奴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我驶兜,道長(zhǎng)扼仲,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任抄淑,我火速辦了婚禮,結(jié)果婚禮上驰后,老公的妹妹穿的比我還像新娘肆资。我一直安慰自己,他們只是感情好灶芝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布郑原。 她就那樣靜靜地躺著唉韭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪犯犁。 梳的紋絲不亂的頭發(fā)上属愤,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音酸役,去河邊找鬼住诸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涣澡,可吹牛的內(nèi)容都是我干的贱呐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼入桂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奄薇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起抗愁,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤馁蒂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜘腌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沫屡,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年逢捺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谁鳍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劫瞳,死狀恐怖倘潜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情志于,我是刑警寧澤涮因,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站伺绽,受9級(jí)特大地震影響养泡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈应,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一澜掩、第九天 我趴在偏房一處隱蔽的房頂上張望碉哑。 院中可真熱鬧宇挫,春花似錦、人聲如沸氏捞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)筐乳。三九已至,卻和暖如春乔妈,著一層夾襖步出監(jiān)牢的瞬間蝙云,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工路召, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勃刨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓优训,卻偏偏與公主長(zhǎng)得像朵你,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子揣非,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • channel一個(gè)類(lèi)型管道抡医,通過(guò)它可以在goroutine之間發(fā)送和接收消息。它是Golang在語(yǔ)言層面提供的go...
    蔡欣圻閱讀 13,506評(píng)論 4 11
  • 介紹 channel 提供了一種通信機(jī)制早敬,通過(guò)它忌傻,一個(gè)goroutine可以向另外一個(gè)goroutine發(fā)送消息。...
    myvic_091閱讀 138評(píng)論 0 0
  • ?? 在golang中搞监,channel屬于較為核心的一個(gè)功能水孩,尤其在go協(xié)程中,channel功能尤為重要琐驴。作為g...
    北春南秋閱讀 6,030評(píng)論 2 2
  • 0. 引言 channel 是 Go 語(yǔ)言中的一個(gè)非常重要的特性俘种,這篇文章來(lái)深入了解一下 channel。 [ht...
    陳sir的知識(shí)圖譜閱讀 1,230評(píng)論 0 0
  • goroutine特性: runtime.Gosched():出讓當(dāng)前cpu時(shí)間片绝淡,當(dāng)再次獲得cpu時(shí)宙刘,從...
    騎蝸上高速閱讀 260評(píng)論 0 0