新手问题 使用channel代替条件变量

themoonstone · 2016年10月14日 · 最后由 astaxie 回复于 2016年10月14日 · 599 次阅读

条件变量信号传递是将条件传达给一个或多个并发任务的方式,在 Go 标准库中'sync'包提供了使用条件变量发送信号的方法。除此之外、在 golang 中另一种方式是使用内置的 channel
两种方式都是暂停当前正在执行的 goroutine 并将 CPU 出让给另一个 goroutine、在此我解释一下为什么我更偏向于使用 channel 而不是条件变量传播信号到我的 worker goroutines。 使用条件变量遇到的问题: 条件变量的两个解除阻塞方法: 1、唤醒一个单一的等待 goroutine 2、唤醒一个所有的等待 goroutine 示例如下:

c := sync.NewCond(&sync.Mutex{})
go func() {
    c.L.Lock()
    c.Wait()
    c.L.Unlock()
    fmt.Println("DONE")
}()
c.Signal()

理论上,goroutine 将在条件创建信号时唤醒。一旦发生这种情况,我们应该看到一个字符串'DONE'出现、但是由于当前的操作系统是抢占式的,所以我们不能保证 s.Signal() 一定会在 goroutine 被完全初始化和阻塞之后才调用 (无法保证程序执行过程中各个 goroutine 的执行顺序)、实际上,对 c.Signal() 的调用可以在 Wait() 之前执行,此时程序将退出、我们什么也看不到、如果我们正在运行的时一个服务、将会有一个 goroutine 处于阻塞状态、该 goroutine 可能会一直阻塞、直到有信号来唤醒它。之所以会出现这样的情况是因为阻塞的 goroutine 可能会错过 Signal() 或者 Broadcast() 方法的唤醒。 而如果你的程序是一个必须 7*24 小时不间断运行的服务的话、这样的情况会使得你的程序 goroutine 持续保持增长直到内存 “爆炸”,而你的操作系统最终会因为内存溢出而终止该进程 通过 channels 在并发 goroutine 中进行信息传递则可以有效避免这样的情况、因为 channel 可以通过超时机制终止 goroutine 运行 正常情况下,每次计算机的操作都需要一段时间,它们会在特定的(也许很少的,也许是频繁的)条件下包含并应该包含停止自身运行的机制。使用 channel 的情况下,我们可以使用 select 语句并添加超时机制。

readyChan := make(chan bool, 1)
go func() {
    select {
        case <- readyChan:
            fmt.Println("HELLO")
        case <- time.After(time.Minute):
    }
}()
readyChan <- true

如代码所示、在使用 channel 的情况下、不会出现 goroutine 一直堆积的情况、如果因为某些原因导致阻塞进程没有被唤醒 、则在一分钟的超时期到的时候、goroutine 会优雅地终止 有一些情况,我们想结束所有等待 goroutines。幸运的是,channel 可以关闭,并保持此状态,以便进一步尝试从中读取:

readyChan := make(chan bool, 1)
for i := 0; i < 5; i++ {
    go func() {
        _, ok := <-readyChan
        if !ok {
            fmt.Println("Worker ending ...")
            return
        }
    }()
}
close(readyChan)
time.Sleep(3*time.Second)

三秒钟后,我们将看到尝试从 channel 中读取数据的五个 goroutine 返回,因为主 goroutine 刚刚关闭了该 channel。 为什么要使用缓冲通道? 这两个示例都使用缓冲 channel,因为如果等待 goroutine 需要很长时间来初始化而且 channel 没有缓冲,发送 goroutine 将锁定并等待,直到 channel 上有一个监听器。 原文链接:https://zeta.si/page/Using-Go-Channels-Instead-Of-Conditions

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册