首页 > 解决方案 > 例行检查频道范围

问题描述

我已经在 Golang 工作了很长时间。但是我仍然面临这个问题,尽管我知道我的问题的解决方案。但一直不明白为什么会这样。

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

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
}

我收到一个死锁错误。为什么在没有 go 例程的情况下使用 range 循环通道会导致死锁。

标签: gochannelgoroutine

解决方案


sq这种情况是由于函数的输出通道没有缓冲造成的。所以sq等到下一个函数从输出中读取,但如果sq不是异步的,它就不会发生(游乐场链接):

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
}

推荐阅读