等待同步.Waitgroup推迟

时间:2016-06-21 11:21:24

标签: go

我从下面的goroutine发送频道时遇到以下代码陷入死锁:

package main

import (
    "fmt"
    "sync"
)

func main() {
    for a := range getCh(10) {
        fmt.Println("Got:", a)
    }
}

func getCh(n int) <-chan int {
    var wg sync.WaitGroup
    ch := make(chan int)
    defer func() {
        fmt.Println("closing")
        wg.Wait()
        close(ch)
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        for i := 0; i < n; i++ {
            ch <- i
        }
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        for i := n; i < 0; i-- {
            ch <- i
        }
    }()

    return ch
}

我知道在wg.Wait()中使用defer是合法的。但我还没有找到一个使用频道作为返回值的函数。

2 个答案:

答案 0 :(得分:5)

我认为您犯的错误是您认为deferred函数也会异步运行。但事实并非如此,因此getCh()将在其延迟部分中阻塞,等待WaitGroup。但由于没有人从通道读取,因此写入它的goroutine无法返回,因此WaitGroup会导致死锁。尝试这样的事情:

func getCh(n int) <-chan int {
    ch := make(chan int)
    go func() {
        var wg sync.WaitGroup
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            for i := 0; i < n; i++ {
                ch <- i
            }
        }(n)
        wg.Add(1)
        go func(n int) {
            defer wg.Done()
            for i := n; i > 0; i-- {
                ch <- i
            }
        }(n)
        wg.Wait()
        fmt.Println("closing")
        close(ch)
    }()

    return ch
}

答案 1 :(得分:1)

由于您没有使用任何缓冲频道,因此您的频道似乎正在阻止。看看这个简单的例子https://play.golang.org/p/zMnfA33qZk

&lt

请记住,频道在填充时会阻止。我不确定你的代码的目标是什么,但看起来你的目标是使用缓冲通道。从有效的go https://golang.org/doc/effective_go.html#channels

开始,这是一个很好的结果
  

接收器始终阻塞,直到有数据要接收为止。如果通道未缓冲,则发送方将阻塞,直到接收方收到该值。如果通道有缓冲区,则发送方仅阻塞,直到将值复制到缓冲区为止;如果缓冲区已满,则表示等待某个接收方检索到一个值。