小编典典

Go中的Python样式生成器

go

我目前正在浏览Go,并且认为goroutines的用法类似于Python生成器,尤其是Question
66
。我以为66看起来很复杂,因此我将其改写为:

package main

import "fmt"

func fibonacci(c chan int) {
    x, y := 1, 1

    for {
        c <- x
        x, y = y, x + y
    }
}

func main() {
    c := make(chan int)
    go fibonacci(c)

    for i := 0; i < 10; i++ {
        fmt.Println(<-c)
    }
}

这似乎有效。几个问题:

  1. 如果我将通道上的缓冲区大小设置为10,fibonacci则将尽快填充另外10个点,并且main会尽快耗尽这些点。这是正确的吗?这会比内存大小为1的缓冲区更好,但是会占用内存,对吗?
  2. 由于通道不会被fibonacci发送方关闭,因此当我们超出范围时在内存方面会发生什么?我的期望是,一旦cgo fibonacci是范围,渠道和东西展现出来就可以得到垃圾收集。我的直觉告诉我,这可能不会发生。

阅读 214

收藏
2020-07-02

共1个答案

小编典典

我喜欢@
tux21b的答案;在fib()函数中创建通道可以使调用代码清晰美观。详细说明一下,如果在调用时无法告诉函数何时停止,则只需要一个单独的“退出”通道。如果您只关心“最多X个数字”,则可以执行以下操作:

package main

import "fmt"

func fib(n int) chan int {
    c := make(chan int)

    go func() {
        x, y := 0, 1

        for x < n {
            c <- x
            x, y = y, x+y
        }

        close(c)
    }()

    return c
}

func main() {
    // Print the Fibonacci numbers less than 500
    for i := range fib(500) {
        fmt.Println(i)
    }
}

如果您想要执行任何一项操作,这有点草率,但是我个人比较喜欢测试呼叫者中的条件,然后通过单独的通道发出退出信号:

func fib(wanted func (int, int) bool) chan int {
    c := make(chan int)

    go func() {
        x, y := 0, 1

        for i := 0; wanted(i, x); i++{
            c <- x
            x, y = y, x+y
        }

        close(c)
    }()

    return c
}

func main() {
    // Print the first 10 Fibonacci numbers
    for n := range fib(func(i, x int) bool { return i < 10 }) {
        fmt.Println(n)
    }

    // Print the Fibonacci numbers less than 500
    for n := range fib(func(i, x int) bool { return x < 500 }) {
        fmt.Println(n)
    }
}

我认为这是否取决于给定情况的具体情况:

  1. 告诉生成器何时停止生成
    1. 传递显式数量的值以生成
    2. 传递目标值
    3. 传递确定是否继续运行的函数
  2. 给生成器一个“退出”通道,自己测试值,并在适当的时候告诉它退出。

总结并实际回答您的问题:

  1. 由于上下文切换较少,因此增加通道大小将有助于提高性能。在这个简单的示例中,性能和内存消耗都不会成为问题,但是在其他情况下,缓冲通道通常是一个很好的主意。make (chan int, 100)在大多数情况下,使用的内存似乎并不重要,但是很容易造成很大的性能差异。

  2. 您的fibonacci函数中有一个无限循环,因此运行它的goroutine将永远运行(c <- x在本例中为on )。您(一旦c超出了调用者的范围)就不会再从与之共享的频道中读取该事实并不会改变这一点。正如@ tux21b所指出的,该通道将永远不会被垃圾回收,因为它仍在使用中。这与关闭通道无关(其目的是让通道的接收端知道不再有值了),并且与不从函数中返回无关。

2020-07-02