go 并发编程
Golang
提供 sync
、channel
两种实现方式支持协程(goroutine)并发。
例如我们举个并发下载资源
的例子,实现两种并发编程:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// ============== sync ==============
startTime1 := time.Now()
testGoroutine()
endTime1 := time.Now()
// 可以看到串行需要 3s 的下载操作,并发后,只需要 1s
fmt.Printf("======> Done! use time: %f",(endTime1.Sub(startTime1).Seconds()))
fmt.Println()
}
// 使用 sync.WaitGroup 多个并发协程之间不能通信, 等待所有并发协程执行结束
var wg sync.WaitGroup
func testGoroutine() {
for i:=0; i<3; i++ {
wg.Add(1) // wg.Add() 为 wg 添加一个计数; wg.Done() 减去一个计数。
go downloadV1("a.com/time_"+string(i+'0')) // 启动新的协程并发执行 download 函数。
}
wg.Wait() // wg.Wait():等待所有的协程执行结束。
}
// ================= 工具 V =================
func downloadV1(url string) {
fmt.Println("start to download", url)
time.Sleep(time.Second) // 模拟耗时
wg.Done()
}
下面我们就来详细说说 两种并发实现方式
sync.WaitGroup(等待组)
在 sync.WaitGroup
类型中,每个WaitGroup
都在内部维护着一个计数(初始值为0)。
如果多个并发协程间不需要通信
,那么非常适合使用 sync.WaitGroup
,等待所有并发协程执行结束。sync.WaitGroup
常用的方法如下:
- (wg * WaitGroup) Add(delta int) 等待组的计数器 +1
- (wg * WaitGroup) Done() 等待组的计数器 -1
- (wg * WaitGroup) Wait() 当等待组计数器不等于 0 时阻塞直到变 0。
sync.WaitGroup(等待组)内部维持着一个计数器
,计数器的值可以通过方法调用实现计数器的增加和减少。当我们添加了 N 个并发任务进行工作时,就将等待组的计数器值增加 N。每个任务完成时,这个值减 1。同时,在另外一个 goroutine 中等待这个等待组的计数器值为 0 时,表示所有任务已经完成。
使用步骤如下:
- 协程开始时使用
wg.Add()
给等待组的计数 +1。 - 协程开始时使用
wg.Done()
给等待组的计数 -1。 wg.Done()
与wg.Add(-1)
完全等价。但如果将 wg 维护的计数更改成负数
,将产生panic(恐慌)
。- 协程调用了
wg.Wait()
时,- 此时 wg 维护的
计数为 0
,则此 wg.Wait() 此操作为空操作(noop)
; - 计数为
正整数
,此协程将进入阻塞状态
等待其他协程执行。当其它协程将此计数更改至 0 时,此协程 wg.Wait() 将返回。
- 此时 wg 维护的