Golang 并发编程(sync实现)

1,816 阅读2分钟

如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。更多往期文章在我的个人博客

go 并发编程

Golang 提供 syncchannel 两种实现方式支持协程(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() 将返回。