goroutine
go语言天然支持高并发, main函数是主goroutine, 在func前使用go关键字即可开启新的goroutine
hello goroutine
func helloGoroutine() {
go func() {
fmt.Printf("hello goroutine \n")
}()
}
func main () {
helloGoroutine()
fmt.Println("main goroutine")
}
运行后发现只打印了main goroutine, 没有打印hello goroutine, 因为开启新goroutine需要耗时, 此时主goroutine已结束运行
func helloGoroutine() {
go func() {
fmt.Printf("hello goroutine \n")
}()
}
func main () {
helloGoroutine()
fmt.Println("main goroutine")
time.Sleep(time.Second)
}
让主goroutine休眠1秒, 即可按预期输出
wait group
实际业务中我们不知道主goroutine需要休眠多久, 此时可用sync.WaitGroup
var wg sync.WaitGroup
func helloGoroutine(index int) {
defer wg.Done() // 函数结束前执行wg计数器减1
go func() {
fmt.Printf("hello goroutine %v \n", index)
}()
}
func main () {
helloGoroutine()
wg.Wait() // 等待所有goroutine全部结束
}
channel
通道是一种类型, 用于goroutine之前通信
var chanA chan int
fmt.Printf("type: %T, value: %#v \n", chanA, chanA) // type: chan int, value: (chan int)(nil)
chanB := make(chan int)
fmt.Printf("type: %T, value: %#v \n", chanB, chanB) // type: chan int, value: (chan int)(0xc000076060)
go func(c <-chan int) { // 只对chan做读操作, 单向通道
res := <-c
fmt.Printf("res: %#v \n", res)
}(chanB)
chanB <- 10 // fatal error: all goroutines are asleep - deadlock! 没有接收者
fmt.Println("send success")
ch1 := make(chan int)
ch2 := make(chan int)
// 开启goroutine将0~100的数发送到ch1中
go func() {
for i := 0; i < 100; i++ {
ch1 <- i
}
close(ch1)
}()
// 开启goroutine从ch1中接收值,并将该值发送到ch2中
go func() {
// 循环从channel中取值
for {
i, ok := <-ch1
if ok {
ch2 <- i
} else {
break
}
}
close(ch2)
}()
// 在主goroutine, 循环从channel中取值
for i := range ch2 { // 通道关闭后会退出for range循环
fmt.Println(i)
}
单向通道
有些通道只能接收数据, 有些通道只能发送数据, 可在chan关键字前后加<-符号
inputChan := make(chan int)
outputChan := make(chan int)
// worker池, 这些worker里的goroutine都处理数据
for workerIndex := 0; workerIndex < 5; workerIndex++ {
go func(workerIndex int, inputChan <-chan int, outputChan chan<- int) {
for inputNum := range inputChan {
fmt.Printf("worker %v, inputNum %v, start \n", workerIndex, inputNum)
time.Sleep(time.Second)
fmt.Printf("worker %v, inputNum %v, end \n", workerIndex, inputNum)
outputChan <- inputNum
}
}(workerIndex, inputChan, outputChan)
}
for num := 0; num < 5; num++ {
inputChan <- num
}
close(inputChan)
for i := 0; i < 5; i++ {
fmt.Printf("outputNum %v \n", <-outputChan)
}
多路复用
对通道可以尝试发送数据或者接收数据
// 多路复用
c := make(chan int, 1)
for i := 0; i < 10; i++ {
select {
case c <- i:
fmt.Printf("通道尝试接收数据成功, 值为%v \n", i)
case x := <-c:
fmt.Printf("通道尝试发送数据成功, 值为%v \n", x)
default:
fmt.Println("默认操作")
}
}