Golang中如何正确等待另一个goroutine的条件触发?
Golang中如何正确等待另一个goroutine的条件触发? 你好,
我是Go语言的新手,目前正在学习并发编程。我需要一种方法,能够同时最多执行两个任务。以下是我编写的代码:
func ExecuteTask(wg *sync.WorkGroup) {
var wg2 sync.WorkGroup
wg2.Add(1)
go CheckTotal(&wg2)
wg2.Wait()
//其余代码部分包含total--,然后执行wg.Done()
}
var total int = 0
var mut sync.Mutex
func CheckTotal(wg *sync.WorkGroup) {
for {
if total < 2 {
mut.Lock()
total++
mut.Unlock()
wg.Done()
}
}
}
func main() {
var wg sync.WorkGroup
wg.Add(10)
for i:=0; i<10; i++ {
ExecuteTask(&wg)
}
wg.Wait()
}
这段代码可以工作,但在CheckTotal函数中使用for循环感觉像是取巧的做法。实现这个功能的正确方式应该是什么?
谢谢
更多关于Golang中如何正确等待另一个goroutine的条件触发?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
通常的做法是使用通道,例如
更多关于Golang中如何正确等待另一个goroutine的条件触发?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中等待条件触发,正确的做法是使用sync.Cond(条件变量)。你的代码存在几个问题:
sync.WorkGroup应该是sync.WaitGroupCheckTotal中的无限循环会消耗大量CPU资源- 竞态条件可能导致逻辑错误
以下是使用sync.Cond的正确实现:
package main
import (
"fmt"
"sync"
"time"
)
type TaskLimiter struct {
cond *sync.Cond
current int
max int
}
func NewTaskLimiter(maxConcurrent int) *TaskLimiter {
return &TaskLimiter{
cond: sync.NewCond(&sync.Mutex{}),
current: 0,
max: maxConcurrent,
}
}
func (tl *TaskLimiter) Acquire() {
tl.cond.L.Lock()
for tl.current >= tl.max {
tl.cond.Wait()
}
tl.current++
tl.cond.L.Unlock()
}
func (tl *TaskLimiter) Release() {
tl.cond.L.Lock()
tl.current--
tl.cond.Signal()
tl.cond.L.Unlock()
}
func ExecuteTask(id int, limiter *TaskLimiter, wg *sync.WaitGroup) {
defer wg.Done()
limiter.Acquire()
defer limiter.Release()
fmt.Printf("Task %d started\n", id)
time.Sleep(time.Second) // 模拟任务执行
fmt.Printf("Task %d completed\n", id)
}
func main() {
const maxConcurrent = 2
const totalTasks = 10
limiter := NewTaskLimiter(maxConcurrent)
var wg sync.WaitGroup
wg.Add(totalTasks)
for i := 0; i < totalTasks; i++ {
go ExecuteTask(i, limiter, &wg)
}
wg.Wait()
fmt.Println("All tasks completed")
}
如果你需要更简单的信号量实现,可以使用channel:
func ExecuteTaskWithChan(id int, sem chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
sem <- struct{}{} // 获取信号量
defer func() { <-sem }() // 释放信号量
fmt.Printf("Task %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Task %d completed\n", id)
}
func main() {
const maxConcurrent = 2
const totalTasks = 10
sem := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup
wg.Add(totalTasks)
for i := 0; i < totalTasks; i++ {
go ExecuteTaskWithChan(i, sem, &wg)
}
wg.Wait()
fmt.Println("All tasks completed")
}
使用channel的版本更符合Go的并发哲学,代码更简洁易懂。sync.Cond版本提供了更多控制,但channel通常是首选方案。

