在Go语言中,开启一个“线程”只需使用go关键字及一个go函数(
goroutine
),goroutine
是廉价且易于创建的,goroutine
不会被运行时垃圾回收,如何确保goroutine
被清理?for...select...
语句提供了可行的解决方案。
一、for...select...
语句
for...select
语句从一组channel中选择可用的进行操作,对于多个可用的channel,select
语句会以随机的方式选择其中一个channel进行操作。- 在子
goroutine
中的for...select...
中添加只读channel(done),对应的处理为return
,即goroutine
退出,在父goroutine
中关闭done
,使done
可用,子goroutine
就会退出。
二、示例代码
package main
import (
"fmt"
"time"
)
func doWork(
done <-chan interface{},
strings <-chan string) <-chan interface{} {
terminated := make(chan interface{})
go func() {
defer fmt.Println("doWork exited!")
defer close(terminated)
for {
select {
case s := <-strings:
fmt.Println(s)
case <-done: // 只读channel,未关闭或未接收数据时不可用(case不选择)
return
}
}
}()
return terminated
}
func main() {
done := make(chan interface{})
strings := make(chan string)
terminated := doWork(done, strings)
go func() {
time.Sleep(time.Second)
fmt.Println("Canceling doWork goroutine...")
close(done) // 关闭done channel之后,在子goroutine中的select语句会选择退出
}()
<-terminated // 阻塞主goroutine
fmt.Println("Done!")
}
// 运行结果:
// Canceling doWork goroutine...
// doWork exited!
// Done!
- 最终子
goroutine
在1 s后退出。在父goroutine
中,对done
channel只进行创建和关闭操作。 - 在
done
未关闭之前,子goroutine
继续执行自身的任务。