如何停止Golang中的goroutines?

如何停止Golang中的goroutines? 你好

我需要在循环中调用 goroutine/线程。由于是循环,会有许多 goroutine 并行执行。如果任何一个 routine/线程执行成功,我就需要停止所有其他线程/routine。

有什么方法可以实现这个吗?

5 回复

循环执行有限的次数,并且 goroutine 会进行 API 调用。

如果有任何 goroutine 成功执行,即 API 成功返回,我必须停止所有其他 goroutine 的执行。

更多关于如何停止Golang中的goroutines?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


根据你的 goroutine 正在执行的任务,有几种实现方式——请记住,goroutine 不能像 Unix 进程那样被“杀死”。必须以某种方式通知它停止。你能描述一下你的 goroutine 将要做什么吗?进行 API 调用?运行长时间的计算?运行一个无限(或非常大)的循环?还是其他什么?

使用 context 是正确的做法:

func doCalls() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    wg := sync.WaitGroup{}
    for _ := range numGoRoutines {
        wg.Add(1)
        go func() {
            defer cancel()
            defer wg.Done()
            client := http.Client{}
            client.Do(ctx, ...)
        }()
    }
    wg.Wait()
}

@maroux,Go 协程只执行最后一个索引。 即 numGoRoutines 为 4 时,只执行最后一个索引(3)。

func apiCalls(ctx context) {
    client := http.Client{}
    client.Do(ctx, …)
}
func doCalls() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    wg := sync.WaitGroup{}
    for _ := range numGoRoutines {
        wg.Add(1)
        go func() {
            defer cancel()
            defer wg.Done()
            apiCalls() //–
        }()
    }
    wg.Wait()
}

在Golang中,你可以使用context包和sync.WaitGroup结合通道来实现这个需求。下面是一个示例:

package main

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

func worker(ctx context.Context, id int, wg *sync.WaitGroup, success chan<- int) {
    defer wg.Done()
    
    select {
    case <-ctx.Done():
        fmt.Printf("Worker %d cancelled\n", id)
        return
    default:
        // 模拟工作
        time.Sleep(time.Duration(id) * time.Second)
        
        // 如果工作成功完成
        fmt.Printf("Worker %d succeeded\n", id)
        select {
        case success <- id:
        default:
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup
    success := make(chan int, 1)
    
    // 启动多个goroutine
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(ctx, i, &wg, success)
    }
    
    // 等待第一个成功的goroutine
    firstSuccess := <-success
    fmt.Printf("Worker %d succeeded first, cancelling others\n", firstSuccess)
    
    // 取消所有其他goroutine
    cancel()
    
    // 等待所有goroutine完成
    wg.Wait()
    close(success)
}

另一个更简洁的版本使用errgroup

package main

import (
    "context"
    "fmt"
    "time"
    "golang.org/x/sync/errgroup"
)

func worker(ctx context.Context, id int) error {
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
        time.Sleep(time.Duration(id) * time.Second)
        fmt.Printf("Worker %d succeeded\n", id)
        return nil
    }
}

func main() {
    g, ctx := errgroup.WithContext(context.Background())
    
    for i := 1; i <= 5; i++ {
        id := i
        g.Go(func() error {
            return worker(ctx, id)
        })
    }
    
    if err := g.Wait(); err != nil {
        fmt.Println("First success completed, others cancelled")
    }
}

注意:第二个示例需要安装golang.org/x/sync/errgroup包。

回到顶部