方案设计及代码实现
- 信号量实现:在Go语言中,我们可以使用
sync.Mutex
和 sync.Cond
来实现信号量,或者直接使用 golang.org/x/sync/semaphore
包。这里我们使用 semaphore
包。
- WaitGroup使用:
sync.WaitGroup
用于等待一组协程完成。我们在创建协程时调用 wg.Add(1)
,在协程结束时调用 wg.Done()
,最后在主协程中调用 wg.Wait()
等待所有协程完成。
package main
import (
"context"
"fmt"
"golang.org/x/sync/semaphore"
"sync"
"time"
)
func main() {
// 最大并发数
maxConcurrent := 3
var wg sync.WaitGroup
sem := semaphore.NewWeighted(int64(maxConcurrent))
// 模拟10个并发任务
taskCount := 10
for i := 0; i < taskCount; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// 获取信号量
err := sem.Acquire(context.Background(), 1)
if err != nil {
fmt.Printf("任务 %d 获取信号量失败: %v\n", id, err)
return
}
defer sem.Release(1)
// 模拟任务执行
fmt.Printf("任务 %d 开始执行\n", id)
time.Sleep(time.Second)
fmt.Printf("任务 %d 执行结束\n", id)
}(i)
}
// 等待所有任务完成
wg.Wait()
fmt.Println("所有任务已完成")
}
信号量和WaitGroup的作用及协同工作
- 信号量的作用:信号量在这里用于限制同时执行某部分操作的协程数量。在上述代码中,
semaphore.NewWeighted(int64(maxConcurrent))
创建了一个最大并发数为 maxConcurrent
的信号量。每个协程在执行关键操作前通过 sem.Acquire(context.Background(), 1)
获取信号量,如果信号量不足则等待,操作完成后通过 sem.Release(1)
释放信号量,这样就保证了同时执行的协程数量不会超过 maxConcurrent
。
- WaitGroup的作用:
sync.WaitGroup
用于同步主协程和其他并发协程。主协程调用 wg.Wait()
进入阻塞状态,直到所有协程都调用了 wg.Done()
,从而确保所有协程完成后程序才结束。
- 协同工作:信号量控制并发执行的协程数量,而WaitGroup确保主协程等待所有协程完成。在每个协程开始时,我们既增加WaitGroup的计数,又获取信号量,协程结束时释放信号量并减少WaitGroup的计数。这样,信号量和WaitGroup共同工作,既保证了并发的合理性,又保证了任务的完整性。