代码之家  ›  专栏  ›  技术社区  ›  Himanshu

按常规通过通道进行测距

  •  0
  • Himanshu  · 技术社区  · 8 年前

    我在戈兰工作了很长时间。尽管我知道解决问题的办法,但我仍然面临着这个问题。但一直没弄明白为什么会这样。

    例如,如果我有如下入站和出站通道的管道情况:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        for n := range sq(sq(gen(3, 4))) {
            fmt.Println(n)
        }
        fmt.Println("Process completed")
    }
    
    func gen(nums ...int) <-chan int {
        out := make(chan int)
        go func() {
            for _, n := range nums {
                out <- n
            }
            close(out)
        }()
        return out
    }
    
    func sq(in <-chan int) <-chan int {
        out := make(chan int)
        go func() {
            for n := range in {
                out <- n * n
            }
            close(out)
        }()
        return out
    }
    

    这不会让我陷入僵局。但如果我删除出站代码中的go例程,如下所示:

    func sq(in <-chan int) <-chan int {
        out := make(chan int)
        for n := range in {
            out <- n * n
        }
        close(out)
        return out
    }
    

    我收到一个死锁错误。为什么使用range without go例程在通道上循环会导致死锁。

    2 回复  |  直到 8 年前
        1
  •  2
  •   Motti    8 年前

    这种情况是由于输出通道 sq 函数没有缓冲。所以 平方米 正在等待下一个函数从输出中读取,但如果 平方米 不是异步的,它不会发生( Playground link ):

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var wg sync.WaitGroup
    
    func main() {
        numsCh := gen(3, 4)
        sqCh := sq(numsCh) // if there is no sq in body - we are locked here until input channel will be closed
        result := sq(sqCh) // but if output channel is not buffered, so `sq` is locked, until next function will read from output channel
    
        for n := range result {
            fmt.Println(n)
        }
        fmt.Println("Process completed")
    }
    
    func gen(nums ...int) <-chan int {
        out := make(chan int)
        go func() {
            for _, n := range nums {
                out <- n
            }
            close(out)
        }()
        return out
    }
    
    func sq(in <-chan int) <-chan int {
        out := make(chan int, 100)
        for n := range in {
            out <- n * n
        }
        close(out)
        return out
    }
    
        2
  •  2
  •   David Maze    8 年前

    函数创建一个通道,写入它,然后返回它。写入将被阻止,直到有人可以读取相应的值,但这是不可能的,因为此函数之外还没有人拥有该通道。

    func sq(in <-chan int) <-chan int {
        // Nobody else has this channel yet...
        out := make(chan int)
        for n := range in {
            // ...but this line will block until somebody reads the value...
            out <- n * n
        }
        close(out)
        // ...and nobody else can possibly read it until after this return.
        return out
    }
    

    如果将循环包装在goroutine中,则循环和 sq 功能允许继续;即使循环阻塞,也会导致 return out 声明仍然可以继续,最终你将能够把一个读者连接到该频道。

    (在goroutines之外的频道上循环本质上没有什么不好的;你的 main 函数以无害且正确的方式执行。)

        3
  •  0
  •   Ron    7 年前

    造成僵局的原因是因为主管道正在等待 sq 返回并完成,但是 平方米 正在等待有人阅读成龙,然后它可以继续。

    我通过删除sq调用层简化了您的代码,并将一句话拆分为两句:

    func main() {
        result := sq(gen(3, 4)) // <-- block here, because sq doesn't return
        for n := range result { 
            fmt.Println(n)
        }
        fmt.Println("Process completed")
    }
    
    func gen(nums ...int) <-chan int {
        out := make(chan int)
        go func() {
            for _, n := range nums {
                out <- n
            }
            close(out)
        }()
        return out
    }
    
    func sq(in <-chan int) <-chan int {
        out := make(chan int)
        for n := range in {
            out <- n * n   // <-- block here, because no one is reading from the chan
        }
        close(out)
        return out
    }
    

    在sq方法中,如果你在 goroutine ,然后 平方米 将返回,main func将不阻塞,并使用结果队列 戈罗廷 将继续,那么就没有阻塞了。

    func main() {
        result := sq(gen(3, 4)) // will not blcok here, because the sq just start a goroutine and return
        for n := range result {
            fmt.Println(n)
        }
        fmt.Println("Process completed")
    }
    
    func gen(nums ...int) <-chan int {
        out := make(chan int)
        go func() {
            for _, n := range nums {
                out <- n
            }
            close(out)
        }()
        return out
    }
    
    func sq(in <-chan int) <-chan int {
        out := make(chan int)
        go func() {
            for n := range in {
                out <- n * n // will not block here, because main will continue and read the out chan
            }
            close(out)
        }()
        return out
    }
    
        4
  •  0
  •   coster    5 年前

    代码有点复杂, 让我们简化一下

    下面的第一个eq,没有死锁

    func main() {
        send := make(chan int)
        receive := make(chan int)
        go func() {
            send<-3
            send<-4
            close(send)
        }()
        go func() {
            receive<- <-send
            receive<- <-send
            close(receive)
        }()
        for v := range receive{
            fmt.Println(v)
    
        }
    }
    

    下面第二个等式,移除“go”已死锁

    func main() {
        send := make(chan int)
        receive := make(chan int)
        go func() {
            send<-3
            send<-4
            close(send)
        }()
        receive<- <-send
        receive<- <-send
        close(receive)
        for v := range receive{
            fmt.Println(v)
    
        }
    }
    

    让我们再次简化第二个代码

    func main() {
        ch := make(chan int)
        ch <- 3
        ch <- 4
        close(ch)
        for v := range ch{
            fmt.Println(v)
    
        }
    }
    

    死锁的原因是主goroutine中没有缓冲通道等待。

    两种解决方案

    // add more cap then "channel<-" time
    func main() {
        ch := make(chan int,2)
        ch <- 3
        ch <- 4
        close(ch)
        for v := range ch{
            fmt.Println(v)
    
        }
    }
    
    //async "<-channel"
    func main() {
        ch := make(chan int)
        go func() {
            for v := range ch {
                fmt.Println(v)
            }
        }()
        ch <- 3
        ch <- 4
        close(ch)
    }