前段时间,在项目中遇到个问题,一直到测试时发现goroutine还在跑,总觉得不对,写了demo跑了发现,果然是有goroutine没有停掉,废话不多说,直接上代码
package main
import (
"fmt"
"time"
)
type Adapter struct {
stop chan struct{}
}
func main() {
ch := make(chan struct{})
a := Adapter{stop: make(chan struct{})}
go a.tick()
time.Sleep(3 * time.Second)
close(a.stop)
a.stop = nil
<-ch
}
func (a *Adapter) tick() {
t := time.Tick(time.Second)
index := 0
for {
select {
case <-t:
fmt.Println("index:", index)
index++
case <-a.stop:
fmt.Println("tick stop")
return
}
}
}
结果,是。。。
index: 0
index: 1
index: 2
index: 3
index: 4
index: 5
index: 6
index: 7
index: 8
index: 9
index: 10
index: 11
index: 12
index: 13
index: 14
index: 15
index: 16
index: 17
index: 18
index: 19
index: 20
...
tick stop没有打印?这个tick协程没有停掉,导致time.Tick这个channel一直在执行。
然后我们在close(a.stop)后插入了一行代码 time.Sleep(1time.Millisecond)*,奇迹发生了
package main
import (
"fmt"
"time"
)
type Adapter struct {
stop chan struct{}
}
func main() {
ch := make(chan struct{})
a := Adapter{stop: make(chan struct{})}
go a.tick()
time.Sleep(3 * time.Second)
close(a.stop)
time.Sleep(1 * time.Millisecond)
a.stop = nil
<-ch
}
func (a *Adapter) tick() {
t := time.Tick(time.Second)
index := 0
for {
select {
case <-t:
fmt.Println("index:", index)
index++
case <-a.stop:
fmt.Println("tick stop")
return
}
}
}
index: 0
index: 1
index: 2
tick stop
上面这个样做视乎有些问题,如果通过close(a.stop)来捕获channel信号,如果这个方法被重复调用,导致会发生错误。
panic: close of closed channel
goroutine 1 [running]:
改进
package main
import (
"fmt"
"log"
"time"
)
type Adapter struct {
stop chan struct{}
}
func main() {
ch := make(chan struct{})
a := Adapter{stop: make(chan struct{})}
go a.tick()
time.Sleep(3 * time.Second)
err := a.closeTick()
if err!=nil{
log.Fatal(err)
}
//test do again
err = a.closeTick()
if err!=nil{
log.Fatal(err)
}
<-ch
}
func (a *Adapter) closeTick() error {
fmt.Println("do closeTick")
if a.stop == nil {
return fmt.Errorf("a.stop is nil")
}
a.stop <- struct{}{}
return nil
}
func (a *Adapter) tick() {
t := time.Tick(time.Second)
index := 0
if a.stop == nil {
fmt.Println("a.stop is nil")
return
}
for {
select {
case <-t:
fmt.Println("index:", index)
index++
case <-a.stop:
fmt.Println("tick stop")
close(a.stop)
a.stop = nil
return
}
}
}
这里没有利用在外部手动close channel,而是给channel传递匿名结构体,使stop接收信号,停掉tick协程
有疑问加站长微信联系(非本文作者)