douchixu3686 2017-02-28 11:00
浏览 42

如何停止goroutine [重复]

This question already has an answer here:

I have a goroutine that calls a function and with a special parameter i want to start or stop this goroutine. My problem is that this code never stops my goroutine, it creates everytime a new job.

quit := make(chan bool)
run := make(chan bool)

    go func() {
        for {
            select {
            case <-quit:
                close(run)
            case <-run:
                myFunc(c)
            default:
            }
        }
    }()

    if x == true {
        quit <- true
    } else {
        run <- true
    }

How do I stop my routine?

</div>
  • 写回答

3条回答 默认 最新

  • doudi1449 2017-02-28 11:08
    关注

    When you close the run channel, case <-run will always trigger: listening on a closed channel returns a zero value immediately.

    if you want to stop the goroutine, you should return after you get the <-quit signal.

    As a side note, your default: clause makes the for loop actively work, you should get rid of it (you will still be listening on both channels)

    评论
  • duan3601 2017-02-28 11:22
    关注

    Here's an isolated commented runable version of how such a signaling system might be implemented.

    package main
    
    import (
        "time"
        "log"
    )
    
    func main() {
        statusChannel := make(chan bool)
        go applicationLoop(statusChannel)
    
        // reasonably random outcome for testing
        if time.Now().Unix() % 2 == 0 {
            statusChannel<-true
        } else {
            statusChannel<-false
        }
    
        for {
            // busy loop for testing
            time.Sleep(1000)
        }
    }
    
    func applicationLoop(statusChannel chan bool) {
        defer close(statusChannel)
        for {
            log.Printf("waiting for signal...
    ")
            shouldContinue := <-statusChannel
            if !shouldContinue {
                log.Print("received false, breaking...
    ")
                break
            }
            // run your code here
            // you should use a second channel to return results, as the channel is not buffered
            log.Print("working...
    ")
        }
    }
    

    Do note that sending a value to statusChannel while is is not listening for a value will make the example blow up in your face. Either use a buffered channel or a channel that signals back to main when the goroutine is back to listening for a signal.

    展开全部

    评论
  • dongzhe3171 2017-02-28 13:15
    关注

    This problem has two parts.

    First we need to stop child goroutines somehow in a way that even if a parent goroutines stops, all it's children should get notified and stop - a hierarchy of stop signals that goes down but not up.

    On the other hand the parent needs to wait for it's children until they are done. Otherwise we would return from a goroutine or even exit from the app before some goroutines are finished properly.

    For simplicity we ignore implementing error handling, timeouts and the like.

    For handling the first problem we use context.Context which gives us a nice hierarchy of execution context handling tools and for solving the second problem we use sync.WaitGroup which allows us to wait for a group of goroutines to complete their tasks. A simple demonstration would be:

    func main() {
        all := &sync.WaitGroup{}
        rootCtx, rootCancel := context.WithCancel(context.Background())
    
        all.Add(1)
        go level1(rootCtx, all)
    
        // just to simulate stop, we could use an os signal instead
        // app ends after 3 seconds
        go func() {
            time.Sleep(time.Second * 3)
            rootCancel()
        }()
    
        all.Wait()
    }
    
    func level1(parent context.Context, all *sync.WaitGroup) {
        defer all.Done()
        l1Ctx, l1Cancel := context.WithCancel(parent)
        defer l1Cancel()
    
        for i := 0; i < 3; i++ {
            all.Add(1)
            go level2(l1Ctx, all)
        }
    
        for {
            select {
            case <-parent.Done():
                return
            // other cases if any,
            // this is a sample
            case <-time.After(time.Second):
                log.Println(`level1`)
            }
        }
    }
    
    func level2(parent context.Context, all *sync.WaitGroup) {
        defer all.Done()
        for {
            select {
            case <-parent.Done():
                return
            case <-time.After(time.Second):
                log.Println(`level2`)
            }
        }
    }
    

    Which gives us some output like:

    [  info ] level2
    [  info ] level2
    [  info ] level2
    [  info ] level1
    [  info ] level2
    [  info ] level1
    [  info ] level2
    [  info ] level2
    

    Currently there is no official package that provide a functionality which combines context.Context and sync.WaitGroup. The nearest thing is an errgroup which can resemble this functionality with some hacks.

    展开全部

    评论
编辑
预览

报告相同问题?

手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部