如何从多个goroutine共享的单个通道读取

时间:2018-10-14 14:34:25

标签: go

我有多个goroutine写入同一通道。如果使用缓冲通道,则可以检索输入。但是,如果使用无缓冲通道,我只能读取大约一半的值:

func testAsyncFunc2() {

    ch := make(chan int,10)

    fmt.Println("testAsyncFunc2")
    wg.Add(10)
    for  i :=0; i < 10; i++  {
        go sender3(ch, i)
        wg.Done()
    }

    receiver3(ch)

    close(ch)
    wg.Wait()
}

这是接收器功能:

func receiver3(ch chan int) {
    for {
        select {
        case <-ch:
            fmt.Println(<-ch)
        default:
            fmt.Println("Done...")
            return
        }
    }
}

发件人功能:

func sender3(ch chan int, i int) {
    ch <- i
}

输出:

  

testAsyncFunc 2 0 4 6 8 2完成...

虽然我希望得到10个号码。

2 个答案:

答案 0 :(得分:3)

选择默认值不能以您认为的方式工作。如果选择块具有默认情况,则在其他情况下都无法读取的情况下,它将立即选择它。

这意味着您的Receiver3很可能会遇到默认情况。

答案 1 :(得分:1)

如果未创建缓冲通道,则代码将返回错误,原因是该通道在发送所有值之前已关闭。

请勿关闭通道并等待执行例程完成。如果要关闭通道,请在接收器执行例程中接收到通道上发送的所有值后关闭。

fmt.Println("testAsyncFunc2")
for  i :=0; i < 10; i++  {
    wg.Add(1)
    go sender3(ch, i)
}

receiver3(ch)
close(ch) // this will close the channels before all the values sent on it will be received.
wg.Wait()

还有另一件事要注意,在for循环内启动go例程后,在减少计数器的同时将等待组的计数器增加到10,这是错误的。完成执行后,应减少sender go例程中的等待组计数器。

func sender(ch chan int, int){
     defer wg.Done()
}

在for循环中,选择默认条件将在接收到通道上发送的所有值之前运行,这就是为什么所有发送的值都不会打印的原因。因为当通道上没有值发送时,循环将返回。

func receiver3(ch chan int) {
    for {
        select {
        case <-ch:
            fmt.Println(<-ch)
        default: // this condition will run when value is not available on the channel.
            fmt.Println("Done...")
            return
        }
    }
}

创建一个go例程以关闭通道,并等待发送者go例程完成。因此,下面的代码将等待所有执行例程完成通道上的值发送,然后关闭通道:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func main() {
    ch := make(chan int)
    fmt.Println("testAsyncFunc2")
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go sender(ch, i)
    }
    receiver3(ch)
    go func() {
        defer close(ch)
        wg.Wait()
    }()
}

func receiver3(ch <-chan int) {
    for i := 0; i < 10; i++ {
        select {
        case value, ok := <-ch:
            if !ok {
                ch = nil
            }
            fmt.Println(value)
        }
        if ch == nil {
            break
        }
    }
}

func sender(ch chan int, i int) {
    defer wg.Done()
    ch <- i
}

输出

testAsyncFunc2
9
0
1
2
3
4
5
6
7
8

Go playground上的工作代码