go routine for range over channels

前端 未结 3 1089
孤街浪徒
孤街浪徒 2021-01-15 17:04

I have been working in Golang for a long time. But still I am facing this problem though I know the solution to my problem. But never figured out why is it happening.

<
相关标签:
3条回答
  • 2021-01-15 17:28

    Your function creates a channel, writes to it, then returns it. The writing will block until somebody can read the corresponding value, but that's impossible because nobody outside this function has the channel yet.

    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
    }
    

    If you wrap the loop in a goroutine then both the loop and the sq function are allowed to continue; even if the loop blocks, the return out statement can still go and eventually you'll be able to connect up a reader to the channel.

    (There's nothing intrinsically bad about looping over channels outside of goroutines; your main function does it harmlessly and correctly.)

    0 讨论(0)
  • 2021-01-15 17:33

    The reason of the deadlock is because the main is waiting for the sq return and finish, but the sq is waiting for someone read the chan then it can continue.

    I simplified your code by removing layer of sq call, and split one sentence into 2 :

    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
    }
    

    In sq method, if you put code in goroutine, then the sq will returned, and main func will not block, and consume the result queue, and the goroutine will continue, then there is no block any more.

    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
    }
    
    0 讨论(0)
  • 2021-01-15 17:35

    This situation caused of output channel of sq function is not buffered. So sq is waiting until next function will read from output, but if sq is not async, it will not happen (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
    }
    
    0 讨论(0)
提交回复
热议问题