一替代
09
1.用函数值
甲生成消费者图案干净多用一个简单的消费者函数传递,其也具有这样的优点,它可以返回一个值信令如果流产或任何其他动作是需要。
而且由于在该示例中只有一个事件要发出信号(“中止”),所以消费者功能将具有返回类型,如果需要中止则发出信号。
所以看到传递给发电机消费函数值这个简单的例子:
func generate(process func(x int) bool) {
for i := 0; i < 10; i++ {
if process(i) {
break
}
}
}
func main() {
process := func(x int) bool {
fmt.Println("Processing", x)
return x == 3 // Terminate if x == 3
}
generate(process)
}
输出(尝试在Go Playground):
Processing 0
Processing 1
Processing 2
Processing 3
processmain()
该解决方案的潜在缺点是它仅使用1个goroutine来生成和消费值。
2。与渠道
for ... range
generate()generate()generate()
而且,如果您要从消费者发信号通知发生器(例如,要中止并且不生成其他值),则可以使用例如,另一个通道,传递给发电机。由于发生器只会“监听”该通道,因此它也可以声明为发生器的只收通道。如果您只需要发送一个事件(在我们的例子中为中止),则不需要在此频道上发送任何值,只需简单的关闭即可。如果您需要发出多个事件的信号,可以通过在此频道上实际发送一个值,执行事件/操作(其中放弃可能来自多个事件中的一个)来完成。
select
abort
func generate(abort <-chan struct{}) <-chan int {
ch := make(chan int)
go func() {
defer close(ch)
for i := 0; i < 10; i++ {
select {
case ch <- i:
fmt.Println("Sent", i)
case <-abort: // receive on closed channel can proceed immediately
fmt.Println("Aborting")
return
}
}
}()
return ch
}
func main() {
abort := make(chan struct{})
ch := generate(abort)
for v := range ch {
fmt.Println("Processing", v)
if v == 3 { // Terminate if v == 3
close(abort)
break
}
}
// Sleep to prevent termination so we see if other goroutine panics
time.Sleep(time.Second)
}
输出(尝试在Go Playground):
Sent 0
Processing 0
Processing 1
Sent 1
Sent 2
Processing 2
Processing 3
Sent 3
Aborting
这种解决方案的明显优点是,它已经使用2个够程( 1生成值,1消耗/处理它们),并且使用任意数量的goroutine处理生成的值非常容易,因为生成器返回的通道可以同时用于多个goroutine - 通道同时可以安全地接收数据,不会发生数据竞争,通过设计;更多阅读:If I am using channels properly should I need to use mutexes?
二,未解决问题的答案
在goroutine上的“未捕获”恐慌将终止goroutine的执行,但不会导致资源泄漏方面的问题。但是,如果作为单独的goroutine执行的函数在非panic情况下释放其分配的资源(在非延迟语句中),那么该代码显然不会运行,并且会导致资源泄漏。
你还没有观察到这一点,因为程序在主要goroutine终止时终止(并且它不会等待其他非主要goroutine完成 - 所以你的其他goroutine没有机会恐慌)。见Spec: Program execution。
panic()recover()try-catchpanic()recover()