关键词
channelpanicselect导语
ChannelGolangGoroutinechannelchannelpanicchannelselectchannel通道发送与接收操作的特点
- 发送操作与接收操作都是互斥的
goroutinechannelgoroutinechannelchannelchannelchannel对通道的发送与接收操作,什么时候会阻塞?
nilchannelmakechannelnilpackage main
import (
"fmt"
"time"
)
func main() {
var ch chan int
go func() {
ch <- 1
fmt.Println("goroutine finish")
}()
time.Sleep(10*time.Second)
}
// main goroutine 会等待10s,不会打印goroutine finish,说明一直阻塞
- 缓冲通道,已满,进行发送操作,发送操作阻塞
package main
import (
"fmt"
"time"
)
func main() {
var ch = make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println("channel is fill")
go func() {
ch <- 4
fmt.Println("fill channel can receive a number?")
}()
time.Sleep(5 * time.Second)
}
- 缓冲通道,已空,进行接收操作,接收操作阻塞
- 非缓冲通道,只有当发送和接收都准备好(其实就是同步),才不会阻塞。
package main
func main() {
// 示例1。
ch1 := make(chan int, 1)
ch1 <- 1
//ch1 <- 2 // 通道已满,因此这里会造成阻塞。
// 示例2。
ch2 := make(chan int, 1)
//elem, ok := <-ch2 // 通道已空,因此这里会造成阻塞。
//_, _ = elem, ok
ch2 <- 1
// 示例3。
var ch3 chan int
//ch3 <- 1 // 通道的值为nil,因此这里会造成永久的阻塞!
//<-ch3 // 通道的值为nil,因此这里会造成永久的阻塞!
_ = ch3
}
panicchannelpackage main
func main() {
var ch = make(chan int, 2)
close(ch)
ch <- 1
}
- 再次关闭已经关闭的通道
package main
func main() {
var ch = make(chan int, 2)
close(ch)
//ch <- 1
close(ch)
}
一个正常的收发channel示例
package main
import "fmt"
func main() {
var ch = make(chan int, 2)
go func() {
for i := 0; i < 10; i++ {
fmt.Println("sender channel a val: ", i)
ch <- i
}
fmt.Println("sender finished, will close the channel")
close(ch)
}()
for {
elem, ok := <- ch
if !ok {
fmt.Println("channel has already closed.")
break
} else {
fmt.Println("receive a val from channel, ", elem)
}
}
fmt.Println("End.")
}
select
基础概念
selectchannelselectcaseselectchannelI/O// 给定几个通道,哪个通道不为空,便执行相应语句
// 准备好几个通道。
intChannels := [3]chan int{
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}
// 随机选择一个通道,并向它发送元素值。
index := rand.Intn(3)
fmt.Printf("The index: %d\n", index)
intChannels[index] <- index
// 哪一个通道中有可取的元素值,哪个对应的分支就会被执行。
select {
case <-intChannels[0]:
fmt.Println("The first candidate case is selected.")
case <-intChannels[1]:
fmt.Println("The second candidate case is selected.")
case elem := <-intChannels[2]:
fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
default:
fmt.Println("No candidate case is selected!")
}
注意点
selectcaseselectdefaultcaseselectselectdefaultevaluatedcasedefaultselectdefaultcaseselectcaseselectcaseforselectcasebreakforselectcaseselectfunc example2() {
intChan := make(chan int, 1)
// 一秒后关闭通道。
time.AfterFunc(time.Second, func() {
close(intChan)
})
select {
case _, ok := <-intChan:
if !ok {
fmt.Println("The candidate case is closed.")
break
}
fmt.Println("The candidate case is selected.")
}
}
// result
The candidate case is closed.
selectcaseintChan1sokfalseThe candidate case is closed.casedefaultpackage main
import "fmt"
var channels = [3]chan int{
nil,
make(chan int),
nil,
}
var numbers = []int{0, 1, 2}
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]
}
// result
No candidate case is selected!
casenildefaultcasepackage main
import "fmt"
var channels = [3]chan int{
nil,
make(chan int),
nil,
}
var numbers = []int{0, 1, 2}
func main() {
go func() {
for {
<- getChan(1)
}
}()
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!")
}
fmt.Println("finish")
}
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]
}
goroutine参考资料
1、极客时间《Go语言核心36讲》