为什么我的函数没有返回?

Why isn't my function returning?

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

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 无限期挂起而不是返回?

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

结束
package main

import "fmt"

func main() {
    var c chan string

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

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

从那里开始,问题就微不足道了。当您启动 goroutine 时,您的频道没有初始化。两个 goroutine 之间存在竞争,显然 go 无法决定哪个协程具有优先权。

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

中有一个完美的例子

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

最相关的点:

  1. 发送到 nil 通道将永远阻塞
  2. 从 nil 通道接收永远阻塞

由于未初始化的channel为nil,因此对其进行任何读写都会导致死锁。例如,在 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 频道接收,这会导致死锁。