为了账号安全,请及时绑定邮箱和手机立即绑定

常规处理频道范围

常规处理频道范围

Go
慕虎7371278 2023-04-24 16:55:29
我在 Golang 工作了很长时间。但是,尽管我知道解决问题的方法,但我仍然面临这个问题。但是从来没有想过为什么会这样。例如,如果我有如下入站和出站通道的管道情况:package mainimport (    "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 routine 的情况下使用 range 循环通道会导致死锁。
查看完整描述

4 回答

?
隔江千里

TA贡献1906条经验 获得超10个赞

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

}


查看完整回答
反对 回复 2023-04-24
?
梦里花落0921

TA贡献1772条经验 获得超5个赞

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


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函数无害且正确地完成了它。)


查看完整回答
反对 回复 2023-04-24
?
ITMISS

TA贡献1871条经验 获得超8个赞

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


下面的第一个 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)


    }

}

下面的第二个 eq,删除“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)


    }

}

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


两种解决方案


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

}


查看完整回答
反对 回复 2023-04-24
?
撒科打诨

TA贡献1934条经验 获得超2个赞

死锁的原因是因为 main 正在等待 returnsq和 finish,而 thesq正在等待有人读取 chan 然后它可以继续。


我通过删除 sq 调用层来简化您的代码,并将一个句子分成 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

}

在sq方法中,如果把代码放入goroutine,willsq返回,main func不会阻塞,消费结果队列,willgoroutine继续,就没有阻塞了。


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

}


查看完整回答
反对 回复 2023-04-24
  • 4 回答
  • 0 关注
  • 125 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信