2016-05-25 4 views
5

私はGolangの同時実行で、数日間にわたって私のコマンドラインユーティリティの1つをリファクタリングすることによってスイングを試みてきましたが、私は立ち往生しています。なぜ私のGolangチャンネル書き込み禁止永遠にですか?

Here's元のコード(マスターブランチ)。

Here's並行性のあるブランチ(x_concurrentブランチ)。

私はgo run jira_open_comment_emailer.goと並行コードを実行するとJIRAの問題は私のwg.Wait()は永遠にハングアップしたチャネルhereに追加された場合、defer wg.Done()が実行されることはありません。

私は大量のJIRAの問題を抱えています。私は応答する必要があるコメントがあるかどうかを確認するために、それぞれについてゴルーチンをスピンオフしたいと考えています。それがある場合は、後でキューのように読むことができて、電子メールリマインダーを構築する構造(いくつかの研究の後にチャネルを選択しました)に追加したいと思います。ゴルーチンがバッファリングされていないチャネルに送信をブロック

// Given an issue, determine if it has an open comment 
// Returns true if there is an open comment on the issue, otherwise false 
func getAndProcessComments(issue Issue, channel chan<- Issue, wg *sync.WaitGroup) { 
    // Decrement the wait counter when the function returns 
    defer wg.Done() 

    needsReply := false 

    // Loop over the comments in the issue 
    for _, comment := range issue.Fields.Comment.Comments { 
     commentMatched, err := regexp.MatchString("~"+config.JIRAUsername, comment.Body) 
     checkError("Failed to regex match against comment body", err) 

     if commentMatched { 
      needsReply = true 
     } 

     if comment.Author.Name == config.JIRAUsername { 
      needsReply = false 
     } 
    } 

    // Only add the issue to the channel if it needs a reply 
    if needsReply == true { 
     // This never allows the defered wg.Done() to execute? 
     channel <- issue 
    } 
} 

func main() { 
    start := time.Now() 

    // This retrieves all issues in a search from JIRA 
    allIssues := getFullIssueList() 

    // Initialize a wait group 
    var wg sync.WaitGroup 

    // Set the number of waits to the number of issues to process 
    wg.Add(len(allIssues)) 

    // Create a channel to store issues that need a reply 
    channel := make(chan Issue) 

    for _, issue := range allIssues { 
     go getAndProcessComments(issue, channel, &wg) 
    } 

    // Block until all of my goroutines have processed their issues. 
    wg.Wait() 

    // Only send an email if the channel has one or more issues 
    if len(channel) > 0 { 
     sendEmail(channel) 
    } 

    fmt.Printf("Script ran in %s", time.Since(start)) 
} 
+3

あなたは 'len(channel)'を全面に持っていますが、そのチャンネルはバッファされていないので長さはありません。送信を完了するためにはチャンネルから受信する必要があります(一般に、バッファリングされたチャンネルの長さに基づいて決定するのは間違いです。同時操作でその値を変更することができます) – JimB

+0

チャンネルへの私の書き込みの、彼らが完了するのを待って、そしてチャンネルから読む...それはsendが実際に完了せず、 'defer wg.Done()'をトリガーするので決して起こり得ないでしょうか?この並行性の実装には、どのように取り組んでいますか? また、 'cap(channel)'のような容量ではなく、チャンネルの現在の要素数を返すというgieceの状態なので、あなたは 'len(channel)'で正しいとは確信していません。 。 https://golang.org/pkg/builtin/#len –

+0

'len(channel)'はバッファされたチャンネルの現在の項目数を返しますが、チャンネルは通常並行して使用されるので、 'len'あなたがそれを読むとすぐに「古く」なります。一般的に、チャネルからの同時のゴルーチンの送受信があります。 Tour Of Goの[Concurrency](https://tour.golang.org/concurrency/1)セクションをもう一度お読みになり、チャンネルの仕組みをよりよく把握することをお勧めします。 – JimB

答えて

8

は、ここでは、コードの該当部分です。

channel := make(chan Issue, len(allIssues)) 

と)(wg.Waitへの呼び出し後にチャンネルを閉じる: 最小限の変更はゴルーチンのブロックを解除すると、すべての問題のために容量のバッファリングされたチャネルを作成することです。

+2

しかし、それは並行ブロック間のパイプとしてチャネルの目的を敗北させるものです。 – RickyA

+0

@RickyAチャネルを使用することで何も問題はありませんが、アイテムのキューです。 –

+0

真実、mutexされたスライスを渡すオーバーヘッドを節約できます。 – RickyA

関連する問題