为什么我的函数没有返回?
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
最相关的点:
- 发送到 nil 通道将永远阻塞
- 从 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)
}
你可以运行上面的例子来说服自己。 (这不是好的做法,甚至不能保证每次都能奏效。)
但是,如果 str := <-c
先发生,那么您正在从 nil
频道接收,这会导致死锁。
下面是启动外部进程的函数,将正则表达式与进程的标准输出进行匹配,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
最相关的点:
- 发送到 nil 通道将永远阻塞
- 从 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)
}
你可以运行上面的例子来说服自己。 (这不是好的做法,甚至不能保证每次都能奏效。)
但是,如果 str := <-c
先发生,那么您正在从 nil
频道接收,这会导致死锁。