如何使用for/select语法从多个通道接收数据?

huangapple go评论78阅读模式
英文:

How to receive data from multiple channels using for/select syntax?

问题

我有一个案例,需要在多个通道上接收数据,并且以某种方式退出无限的for循环。

这是一个简单的例子:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup
	sumChannel := make(chan int)
	productChannel := make(chan int)

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go compute(i, sumChannel, productChannel, &wg)
	}

	go func() {
		wg.Wait()
		close(sumChannel)
		close(productChannel)
	}()

	for {
		select {
		case <-sumChannel:
			fmt.Println("sum")
		case <-productChannel:
			fmt.Println("prod")
		}
	}
}

func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) {
	time.Sleep(2 * time.Second)
	sumChannel <- i + i
	productChannel <- i * i
	wg.Done()
}

问题是我得到了一个无限的for循环。我知道我应该创建一个新的通道(例如quit)来退出循环。但是,我真的不明白在哪里放置quit信号,因为我不知道确切的位置可以确保所有的goroutine都已经完成。

英文:

I have case where I need to receive data on multiple channels and somehow quit infinite for loop.

Here is simple example:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func main() {
	var wg sync.WaitGroup
	sumChannel := make(chan int)
	productChannel := make(chan int)

	for i := 1; i &lt;= 5; i++ {
		wg.Add(1)
		go compute(i, sumChannel, productChannel, &amp;wg)
	}

	go func() {
		wg.Wait()
		close(sumChannel)
		close(productChannel)
	}()

	for {
		select {
		case &lt;-sumChannel:
			fmt.Println(&quot;sum&quot;)
		case &lt;-productChannel:
			fmt.Println(&quot;prod&quot;)
		}
	}
}

func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) {
	time.Sleep(2 * time.Second)
	sumChannel &lt;- i + i
	productChannel &lt;- i * i
	wg.Done()
}

The problem is I get infinite for loop. I know that I should create new channel (for instance,quit) for quitting from loop. But, I really don't understand where to put signal to quit, because I don't know exact place where I can be sure that all goroutines finished.

答案1

得分: 1

如果你在一个go routine中运行for循环,然后等待计算的go routines完成,你可以从那里取消循环:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var wg sync.WaitGroup
	sumChannel := make(chan int)
	productChannel := make(chan int)
	closeChannel := make(chan bool)

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go compute(i, sumChannel, productChannel, &wg)
	}

	go func() {
		loop:
		for {
			select {
			case <-sumChannel:
				fmt.Println("sum")
			case <-productChannel:
				fmt.Println("prod")
			case <-closeChannel:
				fmt.Println("break")
				break loop
			}
		}
	}()

	wg.Wait()
	closeChannel <- true

	fmt.Println("done")
}

func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) {
	time.Sleep(2 * time.Second)
	sumChannel <- i + i
	productChannel <- i * i
	wg.Done()
}

运行它

英文:

If you run the for loop in a go routine and then wait for the compute go routines to finish, you can cancel the loop from there:

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func main() {
	var wg sync.WaitGroup
	sumChannel := make(chan int)
	productChannel := make(chan int)
	closeChannel := make(chan bool)

	for i := 1; i &lt;= 5; i++ {
		wg.Add(1)
		go compute(i, sumChannel, productChannel, &amp;wg)
	}

	go func() {
        loop:
		for {
			select {
			case &lt;-sumChannel:
				fmt.Println(&quot;sum&quot;)
			case &lt;-productChannel:
				fmt.Println(&quot;prod&quot;)
			case &lt;-closeChannel:
				fmt.Println(&quot;break&quot;)
				break loop
			}
		}
	}()

	wg.Wait()
	closeChannel &lt;- true

	fmt.Println(&quot;done&quot;)
}

func compute(i int, sumChannel chan int, productChannel chan int, wg *sync.WaitGroup) {
	time.Sleep(2 * time.Second)
	sumChannel &lt;- i + i
	productChannel &lt;- i * i
	wg.Done()
}

Run it

huangapple
  • 本文由 发表于 2022年7月27日 18:13:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/73136112.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定