转换后的通道

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

Go: transformed channel

问题

假设我在Go语言中有一个int类型的通道:

  1. theint := make(chan int)

我想要在一个名为"incremented"的新通道中包装这个通道:

  1. incremented := make(chan int)

使得以下代码能够正常工作:

  1. go func() { theint <- 1 }()
  2. <- incremented // 2

可以假设只有一个地方从int通道中读取数据。

如果在后台运行一个goroutine,它将能够正常工作:

  1. go func() {
  2. for num := range theint {
  3. incremented <- num + 1
  4. }
  5. }

然而,我更倾向于不使用goroutine来实现,因为我无法在我的上下文中控制它。

有没有更简单的方法来实现这个功能?

我想到的一个方法类似于Python中的yield

  1. for num in theint:
  2. yield num + 1

在Go语言中是否有类似的实现方式?

英文:

Let's say I have an int channel in Go:

  1. theint := make(chan int)

I want to wrap this channel in a new channel called incremented

  1. incremented := make(chan int)

Such that:

  1. go func() { theint &lt;- 1 }
  2. &lt;- 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

  1. go func() {
  2. for num := range theint {
  3. incremented &lt;- num + 1
  4. }
  5. }

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:

  1. for num in theint:
  2. yield num + 1

Is something like this possible in go?

答案1

得分: 2

生成器模式

你正在尝试实现的是生成器模式。在实现这种模式时,使用通道和 goroutine 是非常常见的做法。

然而,我更喜欢不使用 goroutine,因为我无法在我的上下文中控制它。

我相信问题是死锁。

致命错误:所有的 goroutine 都处于休眠状态 - 死锁!

为了避免死锁和未关闭的通道,可以使用 sync.WaitGroup。这是一种控制 goroutine 的惯用方式。

Playground

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. func incGenerator(n []int) chan int {
  7. ch := make(chan int)
  8. var wg sync.WaitGroup
  9. wg.Add(len(n))
  10. for _, i := range n {
  11. incremented := i + 1
  12. go func() {
  13. wg.Done()
  14. ch <- incremented
  15. }()
  16. }
  17. go func() {
  18. wg.Wait()
  19. close(ch)
  20. }()
  21. return ch
  22. }
  23. func main() {
  24. n := []int{1, 2, 3, 4, 5}
  25. for x := range incGenerator(n) {
  26. fmt.Println(x)
  27. }
  28. }
英文:

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

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. )
  6. func incGenerator(n []int) chan int {
  7. ch := make(chan int)
  8. var wg sync.WaitGroup
  9. wg.Add(len(n))
  10. for _, i := range n {
  11. incremented := i + 1
  12. go func() {
  13. wg.Done()
  14. ch &lt;- incremented
  15. }()
  16. }
  17. go func() {
  18. wg.Wait()
  19. close(ch)
  20. }()
  21. return ch
  22. }
  23. func main() {
  24. n := []int{1, 2, 3, 4, 5}
  25. for x := range incGenerator(n) {
  26. fmt.Println(x)
  27. }
  28. }

答案2

得分: 0

你还可以考虑在一个无限循环中使用一个整数通道和一个退出通道。你也可以选择一个变量增量值。请参考下面的代码:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func main() {
  8. var accum int //累加增量值
  9. var wg sync.WaitGroup
  10. c1 := make(chan int)
  11. exChan := make(chan bool)
  12. wg.Add(1)
  13. go func() {
  14. time.Sleep(time.Second * 1)
  15. c1 <- 1
  16. wg.Done()
  17. }()
  18. wg.Add(1)
  19. go func() {
  20. time.Sleep(time.Second * 2)
  21. c1 <- 2
  22. wg.Done()
  23. }()
  24. wg.Add(1)
  25. go func() {
  26. time.Sleep(time.Second * 2)
  27. c1 <- 5
  28. wg.Done()
  29. }()
  30. go func() {
  31. wg.Wait()
  32. close(exChan)
  33. }()
  34. for {
  35. var done bool
  36. select {
  37. case incBy := <-c1: //增加通道中的值
  38. accum += incBy
  39. fmt.Println("接收到的增量值为:", incBy, "; 累加值为", accum)
  40. case d := <-exChan:
  41. done = !(d)
  42. }
  43. if done == true {
  44. break
  45. }
  46. }
  47. fmt.Println("最终累加值为", accum)
  48. }

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:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. &quot;time&quot;
  6. )
  7. func main() {
  8. var accum int //accumulator of incremented values
  9. var wg sync.WaitGroup
  10. c1 := make(chan int)
  11. exChan := make(chan bool)
  12. wg.Add(1)
  13. go func() {
  14. time.Sleep(time.Second * 1)
  15. c1 &lt;- 1
  16. wg.Done()
  17. }()
  18. wg.Add(1)
  19. go func() {
  20. time.Sleep(time.Second * 2)
  21. c1 &lt;- 2
  22. wg.Done()
  23. }()
  24. wg.Add(1)
  25. go func() {
  26. time.Sleep(time.Second * 2)
  27. c1 &lt;- 5
  28. wg.Done()
  29. }()
  30. go func() {
  31. wg.Wait()
  32. close(exChan)
  33. }()
  34. for {
  35. var done bool
  36. select {
  37. case incBy := &lt;-c1: //Increment by value in channel
  38. accum += incBy
  39. fmt.Println(&quot;Received value to increment:&quot;, incBy, &quot;; Accumulated value is&quot;, accum)
  40. case d := &lt;-exChan:
  41. done = !(d)
  42. }
  43. if done == true {
  44. break
  45. }
  46. }
  47. fmt.Println(&quot;Final accumulated value is&quot;, accum)
  48. }

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.

huangapple
  • 本文由 发表于 2017年3月9日 20:38:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/42695608.html
匿名

发表评论

匿名网友

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

确定