为什么我的函数不返回?

时间:2016-05-22 12:11:44

标签: go concurrency channel goroutine

下面是一个启动外部进程的函数,将正则表达式与进程的标准输出进行匹配,并返回匹配的内容。

func (c *Colony) startCircuit(peer *string) (string, error) {
    var (
        err        error
        cmd        *exec.Cmd
        anchorChan chan string
    )

    // ... (omitted for readability)

    // get the anchor from standard output
    go func() {
        defer out.Close()

        anchorChan = make(chan string)
        for scanner := bufio.NewScanner(out); scanner.Scan(); {
            line := scanner.Text()
            if anchor := reRootAnchor.FindString(line); anchor != "" {
                log.Println("Started circuit server with anchor:", anchor)
                anchorChan <- anchor
                break
            }
        }
    }()

    anchor := <-anchorChan
    return anchor, err
}

运行该函数时,我获得了以下输出,表明确实找到了匹配并且(可能)被推入anchorChan

2016/05/22 14:04:36 Started circuit server with anchor: circuit://[::]:36195/20666/Q431cc5fe613aa04b

然而,startCircuit的来电者似乎挂了。以下是相关的代码:

rootAnchor, err := c.startCircuit(peer)
if err != nil {
    return "", err
}
log.Fatal(rootAnchor) // DEBUG

为什么startCircuit无限期悬挂而不是返回?

2 个答案:

答案 0 :(得分:5)

问题其实很简单。提示:以下代码以死锁结尾。

package main

import "fmt"

func main() {
    var c chan string

    go func() {
        c = make(chan string)
        c <- "42"
    }()

    str := <-c
    fmt.Println(str)
}

从那里,问题是微不足道的。启动goroutine时,您的频道未初始化。有两个goroutines的比赛,显然无法决定哪个应该优先考虑。

所以,你的答案是:在goroutine开始之前调用make(chan ...),它应该解决你的问题。在effective go.

中有一个完美的例子

答案 1 :(得分:0)

Dave Cheney有一篇很好的相关博文:http://dave.cheney.net/2014/03/19/channel-axioms

最相关的观点:

  1. 永远发送到nil频道阻止
  2. 来自nil频道的接收永久阻止
  3. 由于未初始化的通道为零,因此对它的任何读写都将导致死锁。例如,在T. Claverie的回答中,有一个竞赛:如果c = make(chan string),(和c <- "42",我认为str := <-c必须在这一点等待)先发生,然后接收发生从初始化的非空通道,一切运行良好:

    package main
    
    import "fmt"
    import "time"
    
    func main() {
        var c chan string
    
        go func() {
            c = make(chan string)
            c <- "42"
        }()
    
        time.Sleep(time.Second * 1)
        str := <-c
        fmt.Println(str)
    }
    

    go playground

    您可以运行上面的示例来说服自己。 (这不是好的做法,甚至保证每次都能奏效。)

    但是,如果首先发生str := <-c,那么您将从nil频道收到,这会导致死锁。