例行检查范围

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.

For example If I have a pipeline situation for inbound and outbound channels like below:

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
}

It does not give me a deadlock situation. But if I remove the go routine inside the outbound code as below:

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

I received a deadlock error. Why is it so that looping over channels using range without go routine gives a deadlock.

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
}

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.)

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
}