先来看一段代码:
type Test struct {
Name string
}
func NewTest(n string) *Test {
return &Test{
Name: n,
}
}
func (t *Test) Start() {
go func() {
for {
print(t.Name)
time.Sleep(1*time.Second)
}
}()
}
func T1() {
t1:=NewTest("t1 ")
t1.Start()
time.Sleep(10*time.Second)
}
func T2() {
t2:=NewTest("t2 ")
t2.Start()
time.Sleep(10*time.Second)
}
func main() {
T1()
T2()
select {
}
}
按常规思维会认为程序输出10个t1和10个t2就不会再输出任何东西了,因为T1()、T2()这两个方法执行完以后,Test的实例就没有任何引用的地方,那么Go的垃圾回收机制就会销毁这两个实例,对应的协程也会销毁了。但事实总是出奇的和预想相反,这个程序会一直输出t1和t2,所以协程中的死循环一定要写一个退出机制。
方案1,设置一个变量来标识循环是否停止:
type Test struct {
Name string
B bool
}
func NewTest(n string) *Test {
return &Test{
Name: n,
B:true,
}
}
func (t *Test) Start() {
go func() {
for t.B{
print(t.Name)
time.Sleep(1*time.Second)
}
}()
}
func T1() {
t1:=NewTest("t1 ")
t1.Start()
time.Sleep(10*time.Second)
t1.B=false
}
func T2() {
t1:=NewTest("t2 ")
t1.Start()
time.Sleep(10*time.Second)
t1.B=false
}
func main() {
T1()
T2()
select {
}
}
方案2,用退出chan:
package main
import "time"
type Test struct {
Name string
t *time.Ticker
quitChan chan struct{
}
}
func NewTest(n string) *Test {
return &Test{
Name: n,
t:time.NewTicker(1*time.Second),
quitChan: make(chan struct{
}),
}
}
func (t *Test) Start() {
go func() {
for {
select {
case <-t.t.C:
print(t.Name)
case <-t.quitChan:
return
}
}
}()
}
func T1() {
t1:=NewTest("t1 ")
t1.Start()
time.Sleep(10*time.Second)
t1.quitChan<- struct{
}{
}
}
func T2() {
t2:=NewTest("t2 ")
t2.Start()
time.Sleep(10*time.Second)
t2.quitChan<- struct{
}{
}
}
func main() {
T1()
T2()
select {
}
}
总之,在go协程中写死循环一定要写退出机制。