停止循环的代码不起作用

时间:2017-09-07 19:04:53

标签: go channel

我正在尝试在Go中实现一个循环停止。 我现在的代码的灵感来自这里: how to kill goroutine

但是,我无法让我的代码按预期运行。复杂代码库中我的代码的简化版本是这样的:

package main

import (
    "fmt"
    "time"
)

var quit chan struct{}

var send chan int

func quitroutine() {
    for {
        select {
        case cnt := <-send:
            fmt.Println(cnt)
            if cnt == 5 {
                quit <- struct{}{}
            }
        }
    }
}

func runLoop() {
    cnt := 0
    for {
        select {
        case <-quit:
            fmt.Println("quit!")
        default:
            fmt.Println("default")
        }
        fmt.Println("inloop")
        time.Sleep(1 * time.Second)
        cnt++
        send <- cnt
    }
}

func main() {
    quit = make(chan struct{})
    send = make(chan int)
    go quitroutine()
    runLoop()
    fmt.Println("terminated")
}

此代码崩溃:

default
inloop
5
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.runLoop()
    /tmp/t.go:37 +0x1a6
main.main()
    /tmp/t.go:45 +0xa4

goroutine 5 [chan send]:
main.quitroutine()
    /tmp/t.go:18 +0x10e
created by main.main
    /tmp/t.go:44 +0x9f
exit status 2

问题:

  1. cnt为5后,为什么会崩溃?如果quitroutine quit只写cnt == 5频道,runLoop,但不会自行终止。 quit quit,如果它在Ex : l = [0,1,2,3,4,5] l = l[0:5] now l is [0,1,2,3,4] 频道收到,则应该打印&#34;退出!&#34; (它没有),但不会自行终止。

  2. 为什么我不能退出&#34;退出!&#34;输出?我甚至可以获得this.refs.dropDown.select(-1)频道吗?

  3. 如何正确实施

2 个答案:

答案 0 :(得分:2)

就像阿德里安所说,你的一个goroutine试图发送quit,而另一个试图发送send。回答你的问题:

  1. cnt == 5 quitroutine开始尝试发送quit时。由于quit <- struct{}{}不是select,因此goroutine会阻止,直到另一个尝试从quit读取。另一个goroutine同样试图做send <- cntcnt = 6时)。

  2. 你永远不会退出&#34;退出!&#34;输出,因为goroutine试图做send <-cnt

  3. 我看到的最简单的解决方案是调整runLoop(),以便send <- cnt中的select成为一个案例。

  4. 我将runLoop()更改为如下所示:

    func runLoop() {
        cnt := 0
        for {
            select {
            case <-quit:
                fmt.Println("quit!")
            case send <- cnt: // moved stuff here
                fmt.Println("inloop")
                time.Sleep(1 * time.Second)
                cnt++
            default:
                fmt.Println("default")
            }
            // stuff used to be here
        }
    }
    

    这给了我输出(直到我杀了程序):

    default
    inloop
    0
    inloop
    1
    inloop
    2
    inloop
    3
    inloop
    4
    inloop
    5
    quit!
    default
    inloop
    6
    inloop
    7
    

    这似乎主要是你所追求的。

    我还要注意select中的quitroutine()块是不必要的,因为它只有一个案例。清理它可能会使goroutine在尝试发送时卡住更加清晰,并且从不接受来自send频道的输入。

答案 1 :(得分:1)

当您尝试发送quit频道时,quitroutine会阻止,直到某些内容为止。

与此同时,runloop中的主例程正在尝试在send频道上发送下一个号码。这也会阻止,因为从中读取的例程当前被阻止尝试发送quit频道。

两个例程都被阻止,这是一个死锁,所以程序崩溃了。

这可以通过将一个或两个通道发送到select中,或者将一个或两个通道缓冲(即使缓冲区长度为1就足够了)来解决。