How to stop goroutine [duplicate]

China☆狼群 提交于 2019-12-10 17:35:33

问题


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?


回答1:


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)




回答2:


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...\n")
        shouldContinue := <-statusChannel
        if !shouldContinue {
            log.Print("received false, breaking...\n")
            break
        }
        // run your code here
        // you should use a second channel to return results, as the channel is not buffered
        log.Print("working...\n")
    }
}

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.




回答3:


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.



来源:https://stackoverflow.com/questions/42516717/how-to-stop-goroutine

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!