原創(chuàng)文章轉(zhuǎn)載請(qǐng)注明出處
Go 運(yùn)行時(shí)(runtime)管理了一種輕量級(jí)線程goroutine
溯街,被叫做協(xié)程诱桂。協(xié)程可以使用共享變量來通信,但是很不提倡這樣做呈昔,因?yàn)檫@種方式給所有的共享內(nèi)存的多線程都帶來了困難挥等。
不要通過共享內(nèi)存來通信,它們會(huì)給你的代碼在并發(fā)運(yùn)算的時(shí)候帶來危險(xiǎn)堤尾,要通過通信來共享內(nèi)存肝劲。
Go有一個(gè)特殊的類型,通道channel
像是管道郭宝,可以通過它們發(fā)送類型化的數(shù)據(jù)在協(xié)程之間通信涡相,可以避開所有內(nèi)存共享導(dǎo)致的坑;通道的通信方式保證了同步性剩蟀。數(shù)據(jù)通過通道:同一時(shí)間只有一個(gè)協(xié)程可以訪問數(shù)據(jù):所以不會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)催蝗,設(shè)計(jì)如此。數(shù)據(jù)的歸屬(可以讀寫數(shù)據(jù)的能力)被傳遞育特。
操作符 <- 被叫做 channel 操作符(這個(gè)操作符中箭頭表明了值的流向)
// 發(fā)送 v 到 channel ch
ch <- v
// 接收 channel ch 中的值并賦值給 v
v := <-ch
燃鵝丙号,channel是會(huì)阻塞的
func main() {
var c1 chan string = make(chan string)
func() {
time.Sleep(time.Second)
c1 <- "1"
}()
fmt.Println("c1 is", <-c1)
}
上面的例子中,push和pop永遠(yuǎn)不可能同時(shí)發(fā)生缰冤,會(huì)deadlock犬缨。
fatal error: all goroutines are asleep -
deadlock
!
goroutine 1 [chan send]:
main.main.func1(0xc42001e0c0)
/Users/mac/gowork/src/hello/hello.go:181 +0x63
main.main()
/Users/mac/gowork/src/hello/hello.go:182 +0x5b
exit status 2
如何實(shí)現(xiàn)非阻塞channel
- 利用
go
關(guān)鍵字,創(chuàng)建一個(gè)新的協(xié)程棉浸,讓push和pop不在同一個(gè)協(xié)程中執(zhí)行就可以避免死鎖怀薛。
func main() {
var c1 chan string = make(chan string)
go func() {
time.Sleep(time.Second)
c1 <- "1"
}()
fmt.Println("c1 is", <-c1)
}
- 也可以給
channel
加一個(gè)buffer,當(dāng)buffer沒有被塞滿的時(shí)候迷郑,channel
是不會(huì)阻塞的枝恋。
func main() {
var c1 chan string = make(chan string, 1)
func() {
time.Sleep(time.Second)
c1 <- "1"
}()
fmt.Println("c1 is", <-c1)
}
- 利用
select
給channel
添加默認(rèn)處理
select
做的就是:選擇處理列出的多個(gè)通信情況中的一個(gè)。
- 如果都阻塞了嗡害,會(huì)等待直到其中一個(gè)可以處理
- 如果多個(gè)可以處理焚碌,隨機(jī)選擇一個(gè)
- 如果沒有通道操作可以處理并且寫了
default
語句,它就會(huì)執(zhí)行:default
永遠(yuǎn)是可運(yùn)行的(這就是準(zhǔn)備好了霸妹,可以執(zhí)行)十电。
在select
中使用發(fā)送操作并且有 default可以確保發(fā)送不被阻塞!如果沒有case
,select
就會(huì)一直阻塞鹃骂。
func main() {
var c1 chan string = make(chan string)
var c2 chan string = make(chan string)
time.Sleep(time.Second)
select {
case c := <-c1:
fmt.Println(c)
case c := <-c2:
fmt.Println(c)
default:
fmt.Println("After one second!")
}
}
- 給
channel
添加超時(shí)處理台盯,還是要用到select
func askForC(c chan string) {
fmt.Println("run in other routine")
}
func main() {
var c1 chan string = make(chan string)
var c2 chan string = make(chan string)
go askForC(c1)
go askForC(c2)
select {
case c := <-c1:
fmt.Println(c)
case c := <-c2:
fmt.Println(c)
case <-time.After(time.Second):
fmt.Println("After one second!")
}
}
我是咕咕雞,一個(gè)還在不停學(xué)習(xí)的全棧工程師畏线。
熱愛生活爷恳,喜歡跑步,家庭是我不斷向前進(jìn)步的動(dòng)力象踊。