这是我参与「第五届青训营 」笔记创作活动的第2天
Go 并发
通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
go 函数名( 参数列表 )
举例
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 100; i++ {
go func(j int) {
fmt.Println(j)
}(i)
}
time.Sleep(time.Second * 10)
}
通道(channel)
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
通道缓冲区
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
watigroup是sync包中的一个struct类型,用来收集需要等待执行完成的goroutine
它有3个方法:
-
add():每次激活想要被等待完成的goroutine之前,先调用add(),用来设置或添加要等待完成的goroutine数量
- 例如add(2)或者两次调用add(1)都会设置等待计数器的值为2,表示要等待2个goroutine完成
-
done():每次需要等待的goroutine在真正完成之前,应该调用该方法来人为表示goroutine完成了,该方法会对等待计数器减1
-
wait():在等待计数器减为0之前,wait()会一直阻塞当前的goroutine
也就是说,add()用来增加要等待的goroutine的数量,done()用来表示goroutine已经完成了,减少一次计数器,wait()用来等待所有需要等待的goroutine完成。
package main
import (
"fmt"
"sync"
"time"
)
func process(i int, wg *sync.waitgroup) {
fmt.println("started goroutine ", i)
time.sleep(2 * time.second)
fmt.printf("goroutine %d ended\n", i)
wg.done()
}
func main() {
no := 3
var wg sync.waitgroup
for i := 0; i < no; i++ {
wg.add(1)
go process(i, &wg)
}
wg.wait()
fmt.println("all go routines finished executing")
}