尝试同时发送和接收时,FanIn 模式会阻塞

问题描述 投票:0回答:1
package main

import (
    "fmt"
)

func main() {
    even := make(chan int)
    odd := make(chan int)
    quit := make(chan int)
    fanin := make(chan int)

    go send(even, odd, quit)

    go receive(even, odd, quit, fanin)

    for v := range fanin {
        fmt.Println(v)
    }

    fmt.Println("about to exit")
}

// send channel
func send(even, odd, quit chan<- int) {
    for i := 0; i < 100; i++ {
        if i%2 == 0 {
            even <- i
        } else {
            odd <- i
        }
    }
    close(quit)
}

// receive channel
func receive(even, odd, quit <-chan int, fanin chan<- int) {
thisLoop:
    for {
        select {
        case fanin <- <-even: //problem is here
        case fanin <- <-odd:
        case <-quit:
            break thisLoop
        }
    }
    close(fanin)
}

这是 Go 中并发扇入模式的糟糕方法的一个例子。在产生所有值的最后和第一种选择的情况下,当范围扇入时会发生死锁:

...
95
96
99
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/floosde/go/src/floosde/main.go:17 +0x194

goroutine 19 [chan receive]:
main.receive(...)
        /home/floosde/go/src/floosde/main.go:41
created by main.main in goroutine 1
        /home/floosde/go/src/floosde/main.go:15 +0x137
exit status 2

但是当我先进行分配而不是同时发送时,它开始工作而没有死锁:

// receive channel
func receive(even, odd, quit <-chan int, fanin chan<- int) {
    thisLoop:
    for {
        select {
        case v := <-even: // Fixed version
            fanin <- v 
        case v := <-odd: // Fixed version
            fanin <- v
        case <-quit:
            break thisLoop
        }
    }
    close(fanin)
}

只是无法理解这种行为的原因。

go concurrency deadlock channel goroutine
1个回答
0
投票

A

select
语句评估发送或接收操作的参数。当你写的时候

case fanin <- <-even:

它首先评估

<-even
,然后检查
fanin
是否准备好接收。如果
even
未准备好发送,则会阻塞。

简而言之,

select
语句为每种情况选择一个通道操作。你正在尝试做两个。

© www.soinside.com 2019 - 2024. All rights reserved.