sync.WaitGroup和sync.Once的爱恨情仇

开发 前端
通过本文,我们了解了Go语言中的两个重要同步工具:​sync.WaitGroup​ 和 ​sync.Once​。​sync.WaitGroup​ 用于等待一组 goroutine 完成,而 ​sync.Once​ 则确保某些操作只执行一次。

今天,我们将继续探讨Go语言中的两个重要的同步工具:sync.WaitGroup 和 sync.Once。

sync.WaitGroup

sync.WaitGroup 是Go语言中的一种计数信号量,用于等待一组 goroutine 完成。它常用于等待一组并发任务全部完成后再继续执行。

使用方法

  1. 声明一个 sync.WaitGroup 类型的变量。
  2. 在每个 goroutine 启动之前调用 Add 方法,增加等待计数。
  3. 在每个 goroutine 完成时调用 Done 方法,减少等待计数。
  4. 在主 goroutine 中调用 Wait 方法,阻塞直到所有 goroutine 完成。

示例代码

package main

import (
   "fmt"
   "sync"
   "time"
)

func worker(id int, wg *sync.WaitGroup) {
   defer wg.Done()
   fmt.Printf("Worker %d starting\n", id)
   time.Sleep(time.Second)
   fmt.Printf("Worker %d done\n", id)
}

func main() {
   var wg sync.WaitGroup

   for i := 1; i <= 5; i++ {
       wg.Add(1)
       go worker(i, &wg)
  }

   wg.Wait()
   fmt.Println("All workers done")
}

在这个例子中,main 函数启动了5个 goroutine,每个 goroutine 都会运行 worker 函数。每个 worker 在完成时调用 wg.Done(),而 main 函数会等待所有 worker 完成后再继续执行。

注意事项

  • WaitGroup 的计数器不能设为负数,否则会引发 panic。
  • 必须确保在所有 Done 调用之前已经调用了 Add。

sync.Once

sync.Once 是一个用于确保某些操作只执行一次的结构体。它提供了一种线程安全的方式来执行一次性初始化操作。

使用方法

  1. 声明一个 sync.Once 类型的变量。
  2. 使用 Do 方法执行需要仅执行一次的操作。

示例代码

package main

import (
   "fmt"
   "sync"
)

func initialize() {
   fmt.Println("Initializing...")
}

func main() {
   var once sync.Once

   for i := 0; i < 10; i++ {
       go func(i int) {
           once.Do(initialize)
           fmt.Printf("Goroutine %d\n", i)
      }(i)
  }

   // 等待所有 goroutine 完成
   var wg sync.WaitGroup
   wg.Add(10)
   for i := 0; i < 10; i++ {
       go func() {
           defer wg.Done()
           once.Do(initialize)
      }()
  }
   wg.Wait()
}

在这个例子中,initialize 函数只会被执行一次,尽管有多个 goroutine 尝试调用 once.Do(initialize)。

注意事项

  • sync.Once 的 Do 方法接受一个无参函数。
  • 即使 Do 方法被多次调用,传入的函数也只会执行一次。

结合使用示例

我们可以结合 sync.WaitGroup 和 sync.Once,来完成一个更复杂的并发任务。假设我们有一个初始化操作,只需执行一次,但在多个 goroutine 中执行其他任务。

示例代码

package main

import (
   "fmt"
   "sync"
   "time"
)

var (
   once sync.Once
   wg   sync.WaitGroup
)

func initialize() {
   fmt.Println("Initializing...")
   time.Sleep(2 * time.Second) // 模拟初始化耗时
   fmt.Println("Initialization complete")
}

func worker(id int) {
   defer wg.Done()
   once.Do(initialize)
   fmt.Printf("Worker %d starting\n", id)
   time.Sleep(time.Second) // 模拟工作
   fmt.Printf("Worker %d done\n", id)
}

func main() {
   const numWorkers = 5
   wg.Add(numWorkers)

   for i := 1; i <= numWorkers; i++ {
       go worker(i)
  }

   wg.Wait()
   fmt.Println("All workers done")
}

在这个例子中,initialize 函数只会执行一次,而 worker 函数会并发执行,等待所有 worker 完成后,程序才会继续执行。

总结

通过本文,我们了解了Go语言中的两个重要同步工具:sync.WaitGroup 和 sync.Once。sync.WaitGroup 用于等待一组 goroutine 完成,而 sync.Once 则确保某些操作只执行一次。这两个工具在实际开发中非常实用,能有效地帮助我们处理并发任务。

责任编辑:武晓燕 来源: 架构师秋天
相关推荐

2024-01-08 13:40:00

并发安全• 数量

2021-08-29 18:13:03

缓存失效数据

2023-06-05 09:23:00

Golang同步工具

2023-10-20 13:35:19

GoWaitGroup

2023-06-06 08:28:58

Sync.OnceGolang

2023-03-28 08:20:07

场景sync.Once​系统

2022-09-02 12:13:22

TCPUDP场景

2022-05-13 09:47:28

Docker容器

2021-04-12 06:08:16

HiveSpark大数据

2019-05-15 15:10:12

Tomcat Session Cookie

2020-11-24 10:13:20

测试开发管理

2024-08-07 08:22:27

2022-06-07 08:55:04

Golang单例模式语言

2024-06-07 00:09:50

2011-08-15 14:06:28

sync中文man

2020-04-09 15:26:55

间谍软件NSOFacebook

2021-06-16 06:48:06

接口微信

2022-05-07 07:43:07

Redis存储系统数据库

2013-02-20 10:00:16

微软CodePlexGitHub

2015-11-24 15:13:15

点赞
收藏

51CTO技术栈公众号