如何阻止goroutine

时间:2017-02-28 19:00:39

标签: go goroutine

我有一个调用函数和特殊参数的goroutine我想开始或停止这个goroutine。我的问题是这段代码永远不会停止我的goroutine,它每次创建一个新工作。

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
    }

如何停止常规?

3 个答案:

答案 0 :(得分:1)

当您关闭run频道时,case <-run将始终触发:在已关闭的频道上收听会立即返回零值。

如果你想停止goroutine,你应该在收到<-quit信号后返回。

作为旁注,你的default:子句使for循环有效,你应该摆脱它(你仍然会在两个频道上听)

答案 1 :(得分:1)

这是一个孤立的评论可运行版本,可以实现这样一个信号系统。

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")
    }
}

请注意,在没有收听值的情况下向statusChannel发送值会使示例在您面前爆炸。当goroutine回到监听信号时,使用缓冲通道或信号回到main的通道。

答案 2 :(得分:1)

这个问题有两个部分。

首先,我们需要以某种方式阻止儿童goroutines,即使父母goroutines停止,所有它的孩子都应该得到通知并停止 - 停止信号的层次结构会下降但不会上升。

另一方面,父母需要等待他们的孩子,直到他们完成为止。否则我们会在一些goroutine正确完成之前从goroutine甚至退出应用程序返回。

为简单起见,我们忽略了实现错误处理,超时等。

为了处理第一个问题,我们使用context.Context为我们提供了一个很好的执行上下文处理工具层次结构,为了解决我们使用sync.WaitGroup的第二个问题,它允许我们等待一组goroutine到完成他们的任务。一个简单的演示将是:

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`)
        }
    }
}

这为我们提供了一些输出:

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

目前,没有官方软件包提供合并context.Contextsync.WaitGroup的功能。最接近的是一个errgroup,它可以像一些黑客一样具有这个功能。