你可以使用sync.Cond来对运行在无限for循环中的goroutine进行单元测试。

huangapple go评论82阅读模式
英文:

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

问题

我正在尝试对一个在无限循环中运行的通道进行单元测试。我认为我找到了一种方法来做到这一点,但我不确定是否这是一种有效使用条件变量的方式。而且我也不确定这种方法是否容易出现竞态条件。由于这个无限循环在自己的goroutine中运行,所以在我到达"cond.Wait()"的时候,通道是否可能已经被耗尽了呢?如果发生这种情况,我会永远挂起吗?在我看到的所有使用条件变量的例子中,它们通常都伴随着一个包围等待的for循环。我需要在这里使用吗?我的问题是:我在这里使用的方法有什么问题吗?这是一种有效/惯用的条件变量的使用方式吗?

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()
	}
}

这段代码的问题在于,当你调用cond.Wait()时,你没有在循环中等待条件的变化。这可能导致你的程序在某些情况下永远挂起。为了解决这个问题,你可以将cond.Wait()放在一个for循环中,以确保在满足条件之前一直等待。修改后的代码如下:

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()
	for result == "" {
		cond.Wait()
	}
	cond.L.Unlock()

	fmt.Println(result)
}

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

现在,当你调用cond.Wait()时,它会在一个for循环中等待,直到result的值被修改为非空。这样,你就可以避免永远挂起的情况。这是一种常见的使用条件变量的方式。

英文:

I am trying to unit test a channel that runs on an infinite for loop. I think I've found a way to do it, but I'm not sure if it's a valid way to use conditional variables. Also I'm not sure if this approach is prone to a race condition. Since the for loop is running on its own goroutine, is it possible the channel would be drained by the time I get to "cond.Wait()?" If this happened would I hang forever? In all the examples I've seen using conditional variables they are usually accompanied by a for loop surrounding the wait. Do I need this here? My question: is there anything wrong with the approach I'm using here? Is this a valid/idiomatic use of conditional variables?

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
)

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

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

	doStuffChan &lt;- true

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

	fmt.Println(result)
}

func startDoStuffLoop() {
	for {
		&lt;-doStuffChan
		result = &quot;success&quot;
		cond.Broadcast()
	}
}

答案1

得分: 2

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

close(doStuffChan)

而不是 doStuffChan <- true,因为你可以永远从关闭的通道接收到 nil。然后在 Wait 周围加上循环,以检查在 cond 成为 true 之前是否已经满足条件,因为在大多数情况下这是一个条件。如果你不想关闭通道,可以使用通道保护信号,并使用锁进行广播,这样可以确定操作的优先级。

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 两个版本都是线程安全的。

英文:

To my mind you are right in all of your assumptions. To avoid channel draining just use

close(doStuffChan)

instead of doStuffChan &lt;- true, because you can receive nil from closed channel forever.
Them surround Wait with loop to check before cond been true since it's a condition in most cases. If you don't want to close channel guard signalling in channel and broadcasting with Lock, which make operation precedence deterministic.

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

	cond.L.Lock()
	doStuffChan &lt;- true
	cond.Wait()
	cond.L.Unlock()

	fmt.Println(result)
}

func startDoStuffLoop() {
	for {
		&lt;-doStuffChan
		result = &quot;success&quot;
		cond.L.Lock()
		cond.Broadcast()
		cond.L.Unlock()
	}
}

See it works https://play.golang.org/p/1S6VW7nIoV Both versions are threadsafe however.

huangapple
  • 本文由 发表于 2016年4月29日 02:33:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/36922831.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定