进阶
goroutine 和 channel 实例
A 线程协程 0~9 个数字
B 线程协程 计算 接受到的数字 的平方
主函数输出 B 协程计算的
package main
import (
"fmt"
)
func main() {
src := make(chan int) // 无缓冲通道
dest := make(chan int, 10) // 有缓冲通道
go func() { // A
defer close(src)
for i := 1; i <= 10; i++ {
src <- i
}
}()
go func() { // B
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
fmt.Println(i)
}
}
-
无缓冲通道 发送方和接收方,必须同时发送和接受,也就是同步
-
有缓冲通道 发送方可以先将东西放到通道,接收方需要了就从通道拿
lock 并发安全
package main
import (
"fmt"
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithlock() {
for i := 0; i < 2e3; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutlock() {
for i := 0; i < 2e3; i++ {
x += 1
}
}
func main() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutlock() // 无锁加法
}
time.Sleep(time.Second)
fmt.Println("无锁:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithlock() // 有锁加法
}
time.Sleep(time.Second)
fmt.Println("有锁:", x)
}
输出:
无锁:9767
有锁:10000
在不加锁的情况下,x 被5个协程同时访问,某些+1加在了错误的 x 上面 因此,共享内存需要特别注意并发安全
WaitGroup 协程计数器
创建一个协程计数器 var wg sync.WaitGroup
协程数量增加 1 个 wg.Add(1)
协程数量减少 1 个 wg.Done()
等待协程数量归零 wg.Wait()
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done() // 当函数结束,代表一个协程结束
fmt.Println(i)
}
func main() {
for i := 0; i < 5; i++ {
wg.Add(1)
go hello(i)
}
wg.Wait() // 等待协程全部结束
}