英文:
Go: transformed channel
问题
假设我在Go语言中有一个int类型的通道:
theint := make(chan int)
我想要在一个名为"incremented"的新通道中包装这个通道:
incremented := make(chan int)
使得以下代码能够正常工作:
go func() { theint <- 1 }()
<- incremented // 2
可以假设只有一个地方从int通道中读取数据。
如果在后台运行一个goroutine,它将能够正常工作:
go func() {
for num := range theint {
incremented <- num + 1
}
}
然而,我更倾向于不使用goroutine来实现,因为我无法在我的上下文中控制它。
有没有更简单的方法来实现这个功能?
我想到的一个方法类似于Python中的yield
:
for num in theint:
yield num + 1
在Go语言中是否有类似的实现方式?
英文:
Let's say I have an int channel in Go:
theint := make(chan int)
I want to wrap this channel in a new channel called incremented
incremented := make(chan int)
Such that:
go func() { theint <- 1 }
<- incremented // 2
appended can be assumed to be the only one that reads from the int.
It will work if a run a goroutine in the background
go func() {
for num := range theint {
incremented <- num + 1
}
}
However, I prefer to do it without a goroutine since I can't control it in my context.
Is there a simpler way to do it?
One thing that came to mind is python's yield
:
for num in theint:
yield num + 1
Is something like this possible in go?
答案1
得分: 2
生成器模式
你正在尝试实现的是生成器模式。在实现这种模式时,使用通道和 goroutine 是非常常见的做法。
然而,我更喜欢不使用 goroutine,因为我无法在我的上下文中控制它。
我相信问题是死锁。
致命错误:所有的 goroutine 都处于休眠状态 - 死锁!
为了避免死锁和未关闭的通道,可以使用 sync.WaitGroup
。这是一种控制 goroutine 的惯用方式。
Playground
package main
import (
"fmt"
"sync"
)
func incGenerator(n []int) chan int {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(len(n))
for _, i := range n {
incremented := i + 1
go func() {
wg.Done()
ch <- incremented
}()
}
go func() {
wg.Wait()
close(ch)
}()
return ch
}
func main() {
n := []int{1, 2, 3, 4, 5}
for x := range incGenerator(n) {
fmt.Println(x)
}
}
英文:
Generator pattern
What you are trying to implement is generator pattern. To use channels and goroutines for implementation of this pattern is totally common practice.
> However, I prefer to do it without a goroutine since I can't control it in my context.
I believe the problem is deadlock
> fatal error: all goroutines are asleep - deadlock!
To avoid deadlocks and orphaned (not closed) channels use sync.WaitGroup
. This is an idiomatic way to control goroutines.
Playground
package main
import (
"fmt"
"sync"
)
func incGenerator(n []int) chan int {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(len(n))
for _, i := range n {
incremented := i + 1
go func() {
wg.Done()
ch <- incremented
}()
}
go func() {
wg.Wait()
close(ch)
}()
return ch
}
func main() {
n := []int{1, 2, 3, 4, 5}
for x := range incGenerator(n) {
fmt.Println(x)
}
}
答案2
得分: 0
你还可以考虑在一个无限循环中使用一个整数通道和一个退出通道。你也可以选择一个变量增量值。请参考下面的代码:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var accum int //累加增量值
var wg sync.WaitGroup
c1 := make(chan int)
exChan := make(chan bool)
wg.Add(1)
go func() {
time.Sleep(time.Second * 1)
c1 <- 1
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 2
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 5
wg.Done()
}()
go func() {
wg.Wait()
close(exChan)
}()
for {
var done bool
select {
case incBy := <-c1: //增加通道中的值
accum += incBy
fmt.Println("接收到的增量值为:", incBy, "; 累加值为", accum)
case d := <-exChan:
done = !(d)
}
if done == true {
break
}
}
fmt.Println("最终累加值为", accum)
}
Playground: https://play.golang.org/p/HmdRmMCN7U
如果我们总是有非零的增量值,那么退出通道是不需要的。我也喜欢@I159的方法!
无论如何,希望这对你有所帮助。
英文:
One thing you can also consider is having a select on the int channel and an exit channel - in an infinite for loop. You can choose a variable increment value too. Please see code below:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var accum int //accumulator of incremented values
var wg sync.WaitGroup
c1 := make(chan int)
exChan := make(chan bool)
wg.Add(1)
go func() {
time.Sleep(time.Second * 1)
c1 <- 1
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 2
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 5
wg.Done()
}()
go func() {
wg.Wait()
close(exChan)
}()
for {
var done bool
select {
case incBy := <-c1: //Increment by value in channel
accum += incBy
fmt.Println("Received value to increment:", incBy, "; Accumulated value is", accum)
case d := <-exChan:
done = !(d)
}
if done == true {
break
}
}
fmt.Println("Final accumulated value is", accum)
}
Playground: https://play.golang.org/p/HmdRmMCN7U
Exit channel not needed, if we are having non-zero increments always. I like @I159 's approach too!
Anyways, hope this helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论