美文网首页
记一次golang中channel的错误使用

记一次golang中channel的错误使用

作者: 包牙齿 | 来源:发表于2019-05-17 21:26 被阅读0次

前段时间,在项目中遇到个问题,一直到测试时发现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协程

相关文章

网友评论

      本文标题:记一次golang中channel的错误使用

      本文链接:https://www.haomeiwen.com/subject/ipjaaqtx.html