如何使用 sync.Cond 对在无限循环上运行的 goroutine 进行单元测试?

How can I unit test a goroutine that runs on an infinite for loop using sync.Cond?

我正在尝试对在无限循环上运行的通道进行单元测试。我想我已经找到了一种方法,但我不确定这是否是使用条件变量的有效方法。另外我不确定这种方法是否容易出现竞争条件。由于 for 循环在其自己的 goroutine 上是 运行,当我到达 "cond.Wait()?" 时,通道是否可能会耗尽?如果发生这种情况,我会永远挂起吗?在我见过的所有使用条件变量的示例中,它们通常伴随着一个围绕等待的 for 循环。我这里需要这个吗?我的问题:我在这里使用的方法有什么问题吗?这是valid/idiomatic条件变量的使用吗?

package main

import (
    "fmt"
    "sync"
)

var doStuffChan chan bool
var cond *sync.Cond
var result string

func main() {
    doStuffChan = make(chan bool, 10)
    cond = &sync.Cond{L: &sync.Mutex{}}
    go startDoStuffLoop()

    doStuffChan <- true

    cond.L.Lock()
    cond.Wait()
    cond.L.Unlock()

    fmt.Println(result)
}

func startDoStuffLoop() {
    for {
        <-doStuffChan
        result = "success"
        cond.Broadcast()
    }
}

在我看来,您的所有假设都是正确的。为避免通道耗尽,只需使用

close(doStuffChan)

而不是 doStuffChan <- true,因为您可以永远从关闭的频道收到 nil。 他们用循环包围 Wait 以在 cond 为真之前检查,因为在大多数情况下这是一个条件。如果您不想关闭频道中的频道守卫信令并使用锁定进行广播,这会使操作优先级确定。

func main() {
    doStuffChan = make(chan bool)
    cond = &sync.Cond{L: &sync.Mutex{}}
    go startDoStuffLoop()

    cond.L.Lock()
    doStuffChan <- true
    cond.Wait()
    cond.L.Unlock()

    fmt.Println(result)
}

func startDoStuffLoop() {
    for {
        <-doStuffChan
        result = "success"
        cond.L.Lock()
        cond.Broadcast()
        cond.L.Unlock()
    }
}

看到它有效https://play.golang.org/p/1S6VW7nIoV但是这两个版本都是线程安全的。