通过context解决goroutine的信息传递

2025-07-10T12:05:14

假如有一个方法一直在打印提示信息,现在想要在main方法中经过一段的时间主动的去停止,这个时候我们可以选择使用共享变量的方式(注意加读写锁),也可以使用channel的方式。例如:

共享变量:

var stop bool
var rwlock sync.RWMutex
var wg sync.WaitGroup
func g1() {
    for {
        rwlock.RLock()
        if stop {
            fmt.Println("退出了捏")
            wg.Done()
            return
        }
        time.Sleep(time.Second)
        fmt.Println("欸嘿!")
        rwlock.RUnlock()
    }
}

func main() {
    wg.Add(1)
    go g1()
    
    time.Sleep(3*time.Second)
    rwlock.Lock()
    stop = true
    rwlock.Unlock()
    wg.Wait()
    fmt.Println("退出程序")
}

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序

使用channel:

var wg sync.WaitGroup

func g1(stop chan struct{}) {
    for {
        select {
        case <- stop:
            fmt.Println("退出了捏")
            wg.Done()
            return
        default:
            time.Sleep(time.Second)
            fmt.Println("欸嘿!")
        }
    }
}

func main() {
    stop := make(chan struct{})
    wg.Add(1)
    go g1(stop)
    time.Sleep(3*time.Second)
    stop <- struct{}{}
    wg.Wait()
    fmt.Println("退出程序")
}
因为channel是协程安全的所以不用加锁

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序

除此之外,还有一种更常用的方式,就是使用context,例如:

var wg sync.WaitGroup

func g1(ctx context.Context) {
    for {
        select {
        case <- ctx.Done():
            fmt.Println("退出了捏")
            wg.Done()
            return
        default:
            time.Sleep(time.Second)
            fmt.Println("欸嘿!")
        }
    }
}

func main() {
    wg.Add(1)
    ctx, cancel := context.WithCancel(context.Background())
    go g1(ctx)
    time.Sleep(3*time.Second)
    cancel()
    wg.Wait()
    fmt.Println("退出程序")
}

运行结果:

欸嘿!
欸嘿!
欸嘿!
退出了捏
退出程序
WithCancel() 方法返回值是一个 context.Context 类型的interface和一个cancel方法
ctx(context.Context)有一个Done方法,返回值是一个channel
当使用cancel方法之后,ctx会向Done()返回的channel中发送一个信号
此时接受到信号,select跳转到退出的分支,由此完成对goroutine的信息传递实现主动退出
当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »