方式一:Sleep

package main

import "time"

func main() {
    go func() {}()

    time.Sleep(time.Second)
}

这种方式,主协程为了等待子协程结束,阻塞了1s时间。

好处是:主协程什么时候解除阻塞,不依赖子协程

缺点是:主协程需要等待多久不好确定

方案二:WaitGroup

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)
    go func() {
        // do something
        //如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
        wg.Done()
    }()
    wg.Wait()
}

这种方式,主协程的解除阻塞条件依赖于子协程

方案三:Channel

func main() {
        // 零缓冲channel
    finish := make(chan struct{})
    go func() {
        // do something
                //如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
        finish <- struct{}{}
    }()
    <-finish
}

这种方式依赖于读取无缓冲通道会阻塞的特性,主协程依赖于子协程

方案四:死循环

package main

import "time"

func main() {
    go func() {
        // do something
    }()
    for {
        time.Sleep(time.Second)
    }
}

死循环里必须sleep,不然的话比较消耗cpu。for {} 是一个 忙等待(busy loop),会导致一个逻辑 CPU 核心持续满载(即使 goroutine 里没有实际工作)

方案五:Select

package main

import "time"

func main() {
    go func() {
        // do something
                // 如果没有一直执行的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
        for {
            time.Sleep(time.Second)
        }
    }()
    select {}
}

通过 select{} 来永久阻塞,但 不消耗 CPU,同时允许其他 goroutine 执行。

方案六:锁

package main

import (
    "sync"
    "time"
)

func main() {
    mutex := sync.Mutex{}
    // 第一次顺利抢到锁
    mutex.Lock()
    go func() {
        // do something
        time.Sleep(5 * time.Second)
                //如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
            mutex.Unlock() // 锁释放
    }()

    // 第二次拿不到锁,等待锁释放
    mutex.Lock()
}
最后修改:2025 年 07 月 05 日
如果觉得我的文章对你有用,请随意赞赏