转换后的通道

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

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 &lt;- 1 }
    &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

go func() {
   for num := range theint {
      incremented &lt;- 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 (
    &quot;fmt&quot;
	&quot;sync&quot;
)

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 &lt;- 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 (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

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 &lt;- 1
		wg.Done()
	}()
	wg.Add(1)
	go func() {
		time.Sleep(time.Second * 2)
		c1 &lt;- 2
		wg.Done()
	}()
	wg.Add(1)
	go func() {
		time.Sleep(time.Second * 2)
		c1 &lt;- 5
		wg.Done()
	}()
	go func() {
		wg.Wait()
		close(exChan)
	}()

	for {
		var done bool

		select {
		case incBy := &lt;-c1: //Increment by value in channel
			accum += incBy
			fmt.Println(&quot;Received value to increment:&quot;, incBy, &quot;; Accumulated value is&quot;, accum)
		case d := &lt;-exChan:
			done = !(d)
		}
		if done == true {
			break

		}
	}

	fmt.Println(&quot;Final accumulated value is&quot;, 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.

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:

确定