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 回复

通常的做法是使用通道,例如

http://jmoiron.net/blog/limiting-concurrency-in-go/

更多关于Golang中如何正确等待另一个goroutine的条件触发?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中等待条件触发,正确的做法是使用sync.Cond(条件变量)。你的代码存在几个问题:

  1. sync.WorkGroup应该是sync.WaitGroup
  2. CheckTotal中的无限循环会消耗大量CPU资源
  3. 竞态条件可能导致逻辑错误

以下是使用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通常是首选方案。

回到顶部