2016-05-22 7 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

ちょうど興味がありますが、なぜ「延期」がゴルーチンにあるのですか? (私はあなたが '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) 
} 

そこから問題は簡単です。あなたのチャンネルは、ゴルーチンを開始するときに初期化されていません。 2つのゴルーチンとの競争があり、どちらが優先されるべきかを決めることはできません。

したがって、あなたの答えは:ゴルーチンが始まる前にmake(chan ...)と呼んで、問題が解決するはずです。この完璧な例がありますeffective go.

+2

私はgoroutineレースの内側に 'str:= <-c'ステートメントをgoroutineの外側に置いて' _'これが正しいとは思いません。したがって、代入はメイン関数のチャンネルに影響し、コードはデッドロックが保証されませんが、レースが含まれているため間違いなく壊れています。レースディテクタがこれをキャッチします。 –

+0

私に間違いを見せてくれてありがとう、私はその問題について間違っていた。 –

0

デイブチェイニー副大統領は良いの関連ブログ記事があります。http://dave.cheney.net/2014/03/19/channel-axioms

最も関連性の高いポイント:

  1. Aは永遠に
  2. Aが受け取るnilのチャネルブロックに送信しますゼロチャネルブロックから永遠にブロック

初期化されていないチャネルはnilなので、それに対するすべての読み書きデッドロックが発生します。たとえば、T. Claverieの答えでは、競争がある:もしc = make(chan string)(と、私は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チャネルから受信し、デッドロックが発生します。

関連する問題