在准备好频道时,从多个频道中选择所有值

时间:2018-02-18 22:38:15

标签: go

我是golang的新人,我遇到了这个问题。

我有几个channel

某些有效负载在不同时间到达此通道。

当通道准备吐出时,如何从通道中逐个获取所有值。

例如我写了这段代码:

package main

import (
    "fmt"
    "time"
    "math/rand"
)

func main() {
    arr1 := []int8{1,2,3,4,5}
    arr2 := []int8{6,7,8,9,10}

    c1 := make(chan int8)
    c2 := make(chan int8)

    go func() {
        for _, val := range arr1 {
            time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
            c1 <- val
        }
    }()
    go func() {
        for _, val := range arr2 {
            time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
            c2 <- val
        }
    }()

    select {
        case res1 := <- c1:
            fmt.Println(res1)
        case res2 := <- c2:
            fmt.Println(res2)
    }

    fmt.Println("Hello, test")
}

但在这种情况下,我从其中一个频道获得第一个值。

请告诉我如何解决我的问题。

链接到去玩https://play.golang.org/p/FOmkP57YCyR

3 个答案:

答案 0 :(得分:2)

你必须做几件事。

1)确保在完成信号源后关闭频道。 2)迭代通道直到它关闭。

示例:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    arr1 := []int8{1, 2, 3, 4, 5}
    arr2 := []int8{6, 7, 8, 9, 10}

    c1 := make(chan int8)
    c2 := make(chan int8)

    go func() {
        for _, val := range arr1 {
            time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
            c1 <- val
        }
        close(c1)
    }()
    go func() {
        for _, val := range arr2 {
            time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
            c2 <- val
        }
        close(c2)
    }()

    _c1 := true
    _c2 := true
    var res1, res2 int8

    for _c1 == true || _c2 == true {
        select {
        case res1, _c1 = <-c1:
            if _c1 == true {
                fmt.Println(res1)
            }
        case res2, _c2 = <-c2:
            if _c2 == true {
                fmt.Println(res2)
            }
        }
    }

    fmt.Println("Hello, test")
}

执行时,我在屏幕上得到了以下输出。

6
1
7
2
3
4
8
5
9
10
Hello, test

答案 1 :(得分:1)

选择不等待go例程。要实现这一点,您应该将其包装在for语句中。这样,select将一直运行,直到其中一个案例返回并突破for语句。

for {
   select {
      ...

您还可以使用非阻塞和等待组的缓冲通道。像这样:

arr1 := []int8{1,2,3,4,5}
arr2 := []int8{6,7,8,9,10}

c1 := make(chan int8, len(arr1))
c2 := make(chan int8, len(arr2))

var wg sync.WaitGroup

wg.Add(1) // First wait group
go func() {
    for _, val := range arr1 {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        c1 <- val
    }
    wg.Done()
}()

wg.Add(1) // Second wait group
go func() {
    for _, val := range arr2 {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        c2 <- val
    }
    wg.Done()
}()

// executed after wg.Done() is called 2 times since we have 2 wait groups
wg.Wait() 

// We are not writing to channels anymore so we can close them.
close(c1)
close(c2)

for value := range c1 {
    fmt.Println(value)
}

for value := range c2 {
    fmt.Println(value)
}


fmt.Println("Hello, test")

答案 2 :(得分:1)

您不必使用2个频道。只需使用1个通道并从多个goroutine中存储值。 Channel是轻量级的线程连接器,速度快,可以多次实例化以存储来自多个goroutine的值。

你的代码的问题是它没有循环来循环来自goroutines通道的值。您只能使用select打印一次。 select让其他goroutines等到它执行一个可能的情况。如果所有情况都可能,那么它随机选择执行。

您只从频道获得一个值的原因是因为当您的goroutine工作时,它们会将数组中的值按顺序存储到频道。当发生这种情况时,您可以在主线程中使用case调用select语句来获取goroutines中通道的值。由于您不会遍历频道,因此您只能从频道获取一个值,即首先收到的值。在这种情况下,您将数组按顺序循环到goroutine中的通道中,因此您将获得数组的第一个索引,因为它是首先发送到主线程中的select语句的值。您可以执行所有选择的案例,因此它将随机执行其中一个案例,并且您将在其中一个数组中获得第一个索引。

要解决此问题,您需要循环通道以逐个获取存储在其中的值。此外,您还需要同步所有线程以避免死锁条件,这种情况发生在您的主线程不知道何时停止从goroutine调用通道时,因为它们异步工作。你的频道准备好在goroutines中获取其值并在循环中调用主线程后立即吐出值。这是代码:

package main

import (
    "fmt"
    "time"
    "math/rand"
    "sync"
)

// writer set numbers from array to channel
func writer(ch chan int, arr []int ,wgwrite *sync.WaitGroup) {
    defer wgwrite.Done()
    for _, val := range arr {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        ch <- val
    }
}

// reader receive input from writer channels and print them all 
func reader(ch chan int, wgread *sync.WaitGroup) {
  defer wgread.Done()
  for i:= range ch {
    fmt.Println(i)
  }
  fmt.Println("Hello, test")
}

func main() {
    arr1 := []int{1,2,3,4,5}
    arr2 := []int{6,7,8,9,10}
    ch := make(chan int)
    wgwrite := &sync.WaitGroup{}
    wgread  := &sync.WaitGroup{}

    wgwrite.Add(2)
    go writer(ch, arr1, wgwrite)
    go writer(ch, arr2, wgwrite)

    wgread.Add(1)
    go reader(ch, wgread)

    wgwrite.Wait()
    close(ch)
    wgread.Wait()
}

https://play.golang.org/p/32Fgetq_Zu7

希望它有所帮助。