我目前正在研究Tour of 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. 如果我调大频道的缓冲器大小,比如说,10fibonacci会尽可能快地填满另外10个点,而main会尽快吃光这些点.这是对的吗?这将比以内存为代价的缓冲区大小1性能更高,对吗?
  2. 由于通道没有被fibonacci发送者关闭,那么当我们超出范围时,在内存方面会发生什么呢?我的预期是,一旦cgo fibonacci超出范围,通道及其上的所有内容都会被垃圾收集.我的直觉告诉我这可能不是事实.

推荐答案

我喜欢@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上的挡路).事实上(一旦c超出了调用方的范围),您就再也不会从与其共享的通道中读取数据了,这并不能改变这一点.正如@tux21b指出的那样,该通道永远不会被垃圾收集,因为它仍在使用中.这与关闭通道(其目的是让通道的接收端知道不会再有值)无关,也与不从函数返回有关.

Go相关问答推荐

区分Terminal和Hook Zerolog Go中的错误级别日志(log)输出

如何模拟嵌入. FS?

仅呈现一个模板的GIN Web应用程序

由docker中的nginx提供的样式和图像在页面上不起作用

埃拉托塞尼筛:加快交叉关闭倍数步骤

日志(log)文件不在 golang 的日志(log)目录中

死锁 - 所有 goroutine 都处于睡眠状态(即使使用等待组)

如何在 Go 服务中导入 monorepo 中的包?

从单词中删除特殊字符

如何使用 go-git 将特定分支推送到远程

helm :将 YAML 转换为 JSON 时出错:yaml:第 xx 行:未找到预期的密钥

泛型:实现嵌套接口

CORS grpc 网关 GoLang

Golang 构建多平台问题

使用 go.work 文件在多个测试文件上运行 go test 命令

如何在 golang revel 中获取动态应用程序配置

如何在 golang 中同时加载 .env 文件和 os 环境变量

如何允许可转换为指针的泛型类型参数化另一种可转换为指针的泛型类型?

如何将类型转换为字节数组golang

如何在 Prometheus 中正确检测区域和环境信息?