返回

等待任务井然有序:Go WaitGroup 并发编程攻略

后端

并发编程的利与弊

在单线程编程中,任务只能一个接一个地执行,当任务数量繁多时,程序执行速度会变得十分缓慢。并发编程应运而生,它允许程序同时执行多个任务,大幅提高程序效率。

然而,并发编程也带来了一些新的挑战,其中之一便是任务同步。当多个任务同时执行时,需要一种机制确保它们在适当的时候以正确的顺序执行,WaitGroup 就是为解决这一问题而诞生的。

WaitGroup 简介

WaitGroup 是 Go 语言内置的一个并发原语,它可以帮助开发者轻松实现任务同步和等待。WaitGroup 的基本原理很简单:它维护着一个计数器,表示需要等待的任务数量。当某个任务完成时,它会调用 WaitGroupDone 方法,将计数器减一。当计数器减至 0 时,WaitGroupWait 方法会返回,表示所有任务都已完成。

WaitGroup 的基本用法

WaitGroup 的基本用法非常简单。首先,创建一个 WaitGroup 对象,然后使用 WaitGroupAdd 方法增加计数器,表示需要等待的任务数量。当某个任务完成时,使用 WaitGroupDone 方法将计数器减一。最后,使用 WaitGroupWait 方法等待所有任务完成。

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup

	// 添加需要等待的任务数量
	wg.Add(2)

	// 创建两个协程,每个协程执行一个任务
	go func() {
		// 模拟任务执行
		fmt.Println("任务 1 完成")
		wg.Done() // 任务完成后,将计数器减一
	}()

	go func() {
		// 模拟任务执行
		fmt.Println("任务 2 完成")
		wg.Done() // 任务完成后,将计数器减一
	}()

	// 等待所有任务完成
	wg.Wait()

	fmt.Println("所有任务完成")
}

WaitGroup 的源码剖析

WaitGroup 的源码非常简洁,只有几十行代码。WaitGroup 的核心数据结构是一个原子整型变量,表示需要等待的任务数量。WaitGroupAddDoneWait 方法都是原子操作,不会被其他协程中断。

type WaitGroup struct {
	state int32
}

func (wg *WaitGroup) Add(delta int) {
	atomic.AddInt32(&wg.state, int32(delta))
}

func (wg *WaitGroup) Done() {
	atomic.AddInt32(&wg.state, -1)
}

func (wg *WaitGroup) Wait() {
	for atomic.LoadInt32(&wg.state) != 0 {
		runtime.Gosched()
	}
}

WaitGroup 的易错盘点

WaitGroup 是一个非常简单的并发原语,但它也有一些容易出错的地方,以下是一些常见的错误:

  • 忘记调用 WaitGroupDone 方法。这会导致 WaitGroup 的计数器永远不会减至 0,导致程序永远不会结束。
  • WaitGroupWait 方法返回之前调用 WaitGroupAdd 方法。这会导致 WaitGroup 的计数器增加,导致程序永远不会结束。
  • WaitGroupWait 方法返回之前调用 WaitGroupDone 方法多次。这会导致 WaitGroup 的计数器减小到负数,导致程序出现异常。

总结

WaitGroup 是一个非常有用的并发原语,它可以帮助开发者轻松实现任务同步和等待。在 Go 并发编程中,WaitGroup 是一个必不可少的工具。

常见问题解答

  1. WaitGroup 如何确保任务以正确的顺序执行?

    WaitGroup 不会确保任务以正确的顺序执行,它只确保所有任务在完成前都已开始。任务的顺序由应用程序的逻辑决定。

  2. WaitGroup 和 channel 有什么区别?

    channel 可以用来传递数据和同步任务,而 WaitGroup 只用于同步任务。channel 更灵活,但使用起来也更复杂。

  3. WaitGroup 和 mutex 有什么区别?

    mutex 可以用来保护共享数据,而 WaitGroup 用于同步任务。mutex 提供更精细的控制,但使用起来也更复杂。

  4. WaitGroup 有什么性能影响?

    WaitGroup 的性能开销很小,它通常不会对应用程序的性能产生显著影响。

  5. 何时应该使用 WaitGroup?

    当需要同步多个任务时,应该使用 WaitGroup。例如,当需要等待一组协程完成时,或者当需要等待一组文件 I/O 操作完成时。