问题描述
package main
import (
"fmt"
"strconv"
"time"
)
func generator() chan int {
ch := make(chan int)
go func() {
i := 0
for {
i++
ch <- i
time.Sleep(time.Duration(10) * time.Millisecond)
}
}()
return ch
}
func printer(delay int,square bool,ch chan int) {
for n := range ch {
if (square) {
fmt.Printf("["+strconv.Itoa(n) + "],")
} else {
fmt.Printf("("+strconv.Itoa(n) + "),")
}
time.Sleep(time.Duration(delay) * time.Millisecond)
}
}
func sendToBoth(ch chan int) (ch1 chan int,ch2 chan int) {
ch1 = make(chan int)
ch2 = make(chan int)
go func() {
for {
//n := <- ch
select {
case v := <- ch: //this is the problem point
ch1 <- v //
case v := <- ch: //
ch2 <- v //
}
}
}()
return ch1,ch2
}
func main() {
ch1,ch2 := sendToBoth(generator())
go printer(100,true,ch1) //[]
go printer(200,false,ch2) //()
var name string
fmt.Scan(&name)
}
我想实现sendToBoth
函数,该函数从通道ch获取生成的数字1,2,3,...
并将其发送到ch1
和ch2
。但是每个人都有不同的延迟,因此我不想一个人等待其他人解锁,所以我尝试使用select
,但不知道如何询问ch1
或{{ 1}}目前在case子句中可用。有什么帮助吗?
输出应该像
ch2
解决方法
所以我要说的第一个反应是“只要让他们同步运行,就会容易得多。”
func sendToBoth(ch chan int) (ch1,ch2 chan int) {
ch1 = make(chan int)
ch2 = make(chan int)
go func() {
defer close(ch1)
defer close(ch2)
for n := range ch {
ch1 <- n
ch2 <- n
}
}()
return ch1,ch2
}
那么简单,我喜欢它!但是,假设您希望ch1和ch2各自消耗自己的速率。如果您希望它们彼此分开,则必须使用临时存储来解决。 easy 的出路是只给通道一些缓冲空间:
ch1 = make(chan int,10)
ch2 = make(chan int,10)
现在,ch1可以运行得更快-但它只能比ch2快10个项目。反之亦然。
如果要使用大小不受限制的缓冲区,则必须自己保留。我们可以利用以下事实:nil
通道完全可以用作select
分支:
func sendToBoth(ch chan int) (ch1,ch2 chan int) {
ch1 = make(chan int)
ch2 = make(chan int)
go func() {
defer close(ch1)
defer close(ch2)
var arr []int
var pos1,pos2 int
ich := ch
for inch != nil && (pos1 < len(arr) || pos2 < len(arr)) {
var och1,och2 chan int
var v1,v2 int
if pos1 < len(arr) {
och1 = ch1
v1 = arr[pos1]
}
if pos2 < len(arr) {
och2 = ch2
v2 = arr[pos2]
}
select {
case n,ok := <- ich:
if !ok {
ich = nil // done
} else {
arr = append(arr,n)
}
case och1 <- v1:
pos1++
case och2 <- v2:
pos2++
}
}
}()
return ch1,ch2
}
有点复杂-我不完全确定这是正确的。另外请注意,我们并没有为存储流中的旧项目释放存储空间。
,如果来自ch1
和ch2
的值以不同的速率消耗,并且您希望分配给两者而不必等待另一个,则sendToBoth()
必须缓冲值,直到它们被接收。在您的示例中,此缓冲区将不断增长。您要如何处理?
实现此缓冲的一种非常简单合理的方法是创建,使用和返回缓冲的通道:
func sendToBoth(ch chan int) (ch1 chan int,ch2 chan int) {
ch1 = make(chan int,100)
ch2 = make(chan int,100)
go func() {
for n := range ch {
ch1 <- n
ch2 <- n
}
}()
return ch1,ch2
}
仅此而已。只要它们的缓冲区未满,在ch1
和ch2
上的发送将不会阻塞。当然,如果ch1
或ch2
的缓冲区之一已满,则进一步的发送将不得不等待,直到收到来自它的元素,但这必须是可以接受的折衷方案,否则对缓冲元素的增长没有限制。您可以使用任何希望的缓冲区大小。
进行此更改后,输出将如您所愿:
[1],(1),[2],(2),[3],[4],(3),[5],[6],(4),[7],[8],(5),[9],[10],(6),[11],[12],(7),[13],[14],(8),[15],[16],(9),[17],[18],