2024年10月28日星期一

golang 的sync.WaitGroup 和信号量是什么意思?

 sync.WaitGroup信号量是Golang和并发编程中常用的概念,帮助我们管理和控制并发任务的执行顺序、数量以及同步。

1. sync.WaitGroup 简介

sync.WaitGroup是Golang标准库中的一个同步原语,用于等待一组协程(goroutine)完成工作。在多个并发任务执行时,它可以帮助主协程等待所有任务完成再继续执行或退出。

使用方法

sync.WaitGroup 的工作原理是计数:

  • 增加计数:通过调用Add(n)增加计数,表示将要执行n个任务。
  • 减少计数:每当一个任务完成时,调用Done()减少计数。
  • 等待完成:调用Wait()阻塞等待,直到计数归零,表示所有任务已完成。

示例

假设我们要启动3个并发任务,并等待它们都完成:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    // 启动3个协程
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 增加计数

        go func(i int) {
            defer wg.Done() // 减少计数
            fmt.Printf("Task %d is done\n", i)
        }(i)
    }

    // 等待所有任务完成
    wg.Wait()
    fmt.Println("All tasks completed.")
}
  
在这个例子中,wg.Add(1)增加计数,wg.Done()减少计数,而wg.Wait()会阻塞,直到所有任务完成后解除阻塞并继续执行。

2. 信号量(Semaphore)

信号量是一种用于控制资源访问的并发机制。通过信号量可以限制并发任务的最大数量(即资源的使用量),在实际应用中,例如限制并发处理的协程数量。

在Golang中,可以用带缓冲的通道(chan struct{})来实现一个简单的信号量。

实现信号量的关键原理

  • 缓冲通道的容量:将通道的缓冲容量设置为允许的最大并发数量(例如,3)。
  • 获取信号量:每个任务开始时向通道发送一个信号(sem <- struct{}{}),表示该资源正在被占用。
  • 释放信号量:任务完成后从通道读取一个信号(<-sem),表示资源已释放。

示例

假设我们想同时只允许3个协程运行,超出的任务需要等待:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建容量为3的信号量通道
    sem := make(chan struct{}, 3)

    for i := 1; i <= 5; i++ {
        // 获取信号量
        sem <- struct{}{}

        go func(i int) {
            defer func() { <-sem }() // 释放信号量
            fmt.Printf("Task %d is starting\n", i)
            time.Sleep(2 * time.Second) // 模拟任务耗时
            fmt.Printf("Task %d is done\n", i)
        }(i)
    }

    // 等待所有任务完成
    time.Sleep(6 * time.Second)
    fmt.Println("All tasks completed.")
}
------console.log-----
Task 3 is starting
Task 1 is starting
Task 2 is starting
Task 1 is done
Task 3 is done
Task 2 is done
Task 4 is starting
Task 5 is starting
Task 5 is done
Task 4 is done
All tasks completed.

在这个例子中,最多只允许3个任务同时运行。当任务完成后,它们会从信号量通道中释放一个信号,让其他等待中的任务可以启动。


sync.WaitGroup信号量(Semaphore)都是用于并发处理的机制

但它们解决的问题和适用的场景略有不同:

  1. sync.WaitGroup:是一种同步机制,用于等待一组并发任务完成,适合用来控制并发任务的生命周期。WaitGroup的作用是确保多个协程(goroutine)都执行完成后,再继续执行后续的逻辑,但它并不限制并发任务的数量

    • 适用场景:需要等待一组并发任务完成才能进行下一步时使用。例如在主协程中等待所有子协程的任务完成后再退出。

    • 核心操作

      • 增加计数 Add(n)
      • 减少计数 Done()
      • 等待计数归零 Wait()
  2. 信号量(Semaphore):是一种并发控制机制,用于限制同时运行的并发任务数量。在Golang中,通过带缓冲的通道来实现信号量,控制并发资源的访问。

    • 适用场景:限制并发数量,确保同时运行的任务不会超过某个上限。例如,限制最大并发处理数量为3的场景。

    • 核心操作

      • 获取信号量:向缓冲通道发送信号(表示资源被占用)
      • 释放信号量:从缓冲通道读取信号(表示资源被释放)

总结

  • sync.WaitGroup:管理并发任务完成的同步,不限制数量。
  • 信号量:控制并发任务的数量,限制资源使用。

两者结合使用时可以既控制任务并发数(信号量),又确保所有任务完成后再继续(WaitGroup)。

没有评论: