2016-05-22 48 views
1

下面是一个函数,用于启动外部进程,将正则表达式与进程的标准输出进行匹配,并返回匹配的内容。为什么我的函数没有返回?

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

+0

只是好奇,为什么'延期'是在goroutine? (我假设你在'starCircuit'函数中打开文件) – AkiRoss

+0

另外,你能提供一个MWE吗?我有点好奇地尝试自己:) – AkiRoss

+0

@ T.Claverie你已经删除了你的答案,但是你建议在goroutine之外移动'make'调用似乎解决了这个问题。随时重新提交您的答案! – blz

回答

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时未初始化。这两个门厅有一场比赛,显然不能决定哪一个应该优先考虑。

那么,你的答案是:拨打make(chan ...)的够程启动之前,它应该解决您的问题。有这样一个完美的例子effective go.

+2

我不认为这是_quite_正确的 - 在goroutine比赛中用'str:= <-c'语句在goroutine之外对'c'的赋值。因此,赋值确实会影响主函数中的通道,并且不保证代码会死锁,但它肯定会因为它包含竞争而中断。比赛探测器将抓住这一点。 –

+0

感谢您向我展示我的错误,我误解了这个问题。 –

0

戴夫·切尼具有良好的相关博客文章:http://dave.cheney.net/2014/03/19/channel-axioms

最相关的要点:

  1. 一个发送到零通道块永远
  2. ,接收从零通道块永远

由于未初始化的通道是零,因此任何读取和写入它将导致僵局。例如,在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渠道接收到,这会导致死锁。

相关问题