首页 > 后端开发 > Golang > Go 中的生成器并发模式:综合指南

Go 中的生成器并发模式:综合指南

Mary-Kate Olsen
发布: 2025-01-04 06:16:40
原创
293 人浏览过

⚠️ 这个系列如何进行?

1。运行每个示例:不要只阅读代码。输入它,运行它,然后观察其行为。
2。实验和打破常规: 删除睡眠并看看会发生什么,更改通道缓冲区大小,修改 goroutine 计数。
打破东西会教你它们是如何工作的
3。关于行为的原因: 在运行修改后的代码之前,尝试预测结果。当您看到意外行为时,请停下来思考原因。挑战解释。
4。建立心理模型:每个可视化代表一个概念。尝试为修改后的代码绘制自己的图表。

Generator Concurrency Pattern in Go: A Comprehensive Guide

在上一篇文章中,我们探讨了 goroutine 和通道的基础知识,它们是 Go 并发的构建块。阅读此处:

Generator Concurrency Pattern in Go: A Comprehensive Guide

理解和可视化 Golang 中的 Goroutine 和 Channel

Souvik Kar Mahapatra ・ 12 月 20 日

#去 #编程 #学习 #教程

现在,让我们看看这些原语如何组合起来形成解决现实世界问题的强大模式。

在这篇文章中,我们将介绍生成器模式并尝试将它们可视化。因此,让我们做好准备,因为我们将亲手完成整个过程。

Generator Concurrency Pattern in Go: A Comprehensive Guide

生成器模式

生成器就像一个喷泉,不断产生我们可以在需要时使用的值。

在 Go 中,它是一个生成值流并通过通道发送它们的函数,允许程序的其他部分按需接收这些值。

Generator Concurrency Pattern in Go: A Comprehensive Guide

让我们看一个例子:

// generateNumbers creates a generator that produces numbers from 1 to max
func generateNumbers(max int) chan int {
    // Create a channel to send numbers
    out := make(chan int)

    // Launch a goroutine to generate numbers
    go func() {
        // Important: Always close the channel when done
        defer close(out)

        for i := 1; i <= max; i++ {
            out <- i  // Send number to channel
        }
    }()

    // Return channel immediately
    return out
}

// Using the generator
func main() {
    // Create a generator that produces numbers 1-5
    numbers := generateNumbers(5)

    // Receive values from the generator
    for num := range numbers {
        fmt.Println("Received:", num)
    }
}
登录后复制
登录后复制

在这个例子中,我们的生成器函数做了三件关键的事情:

  1. 创建一个通道来发送值
  2. 启动 goroutine 来生成值
  3. 立即返回频道供消费者使用

为什么使用生成器?

  1. 将价值生产与消费分开
  2. 按需生成值(惰性求值)
  3. 可以表示无限序列而不消耗无限内存
  4. 允许并发生产和消费值

现实世界的用例

逐行读取大文件:

func generateLines(filename string) chan string {
    out := make(chan string)
    go func() {
        defer close(out)
        file, err := os.Open(filename)
        if err != nil {
            return
        }
        defer file.Close()

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            out <- scanner.Text()
        }
    }()
    return out
}
登录后复制
登录后复制

现在您可能会想,它有什么特别之处?我们可以做同样的事情,比如生成数据序列或在没有 goroutine 的情况下逐行读取。是不是太过分了?让我们尝试可视化这两种情况:

没有 goroutine

// Traditional approach
func getNumbers(max int) []int {
    numbers := make([]int, max)
    for i := 1; i <= max; i++ {
        numbers[i-1] = i
        // Imagine some heavy computation here
        time.Sleep(100 * time.Millisecond)
    }
    return numbers
}
登录后复制
登录后复制

这里需要等待一切准备就绪才可以开始处理。

带有 goroutine

// Generator approach
func generateNumbers(max int) chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for i := 1; i <= max; i++ {
            out <- i
            // Same heavy computation
            time.Sleep(100 * time.Millisecond)
        }
    }()
    return out
}
登录后复制
登录后复制

您可以在数据仍在生成时开始处理数据。

Generator Concurrency Pattern in Go: A Comprehensive Guide

生成器模式的主要优点:

  1. 非阻塞执行:生成和处理同时发生

  2. 内存效率:一次可以生成并处理一个值,无需立即存储在内存中

  3. 无限序列:可以生成无限序列而不会出现内存问题

  4. 反压处理:如果你的消费者很慢,生成器自然会减慢(由于通道阻塞),防止内存过载。

// generateNumbers creates a generator that produces numbers from 1 to max
func generateNumbers(max int) chan int {
    // Create a channel to send numbers
    out := make(chan int)

    // Launch a goroutine to generate numbers
    go func() {
        // Important: Always close the channel when done
        defer close(out)

        for i := 1; i <= max; i++ {
            out <- i  // Send number to channel
        }
    }()

    // Return channel immediately
    return out
}

// Using the generator
func main() {
    // Create a generator that produces numbers 1-5
    numbers := generateNumbers(5)

    // Receive values from the generator
    for num := range numbers {
        fmt.Println("Received:", num)
    }
}
登录后复制
登录后复制

常见陷阱和解决方案

  1. 忘记关闭频道
func generateLines(filename string) chan string {
    out := make(chan string)
    go func() {
        defer close(out)
        file, err := os.Open(filename)
        if err != nil {
            return
        }
        defer file.Close()

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            out <- scanner.Text()
        }
    }()
    return out
}
登录后复制
登录后复制
  1. 不处理错误
// Traditional approach
func getNumbers(max int) []int {
    numbers := make([]int, max)
    for i := 1; i <= max; i++ {
        numbers[i-1] = i
        // Imagine some heavy computation here
        time.Sleep(100 * time.Millisecond)
    }
    return numbers
}
登录后复制
登录后复制
  1. 资源泄漏:将生成器与资源(如文件)一起使用时,请确保正确清理:
// Generator approach
func generateNumbers(max int) chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for i := 1; i <= max; i++ {
            out <- i
            // Same heavy computation
            time.Sleep(100 * time.Millisecond)
        }
    }()
    return out
}
登录后复制
登录后复制

这就是生成器模式的全部内容。接下来是管道并发模式。请继续关注,清除您对 Golang 并发的概念。

我错过了什么吗?有疑问吗?有什么有趣的事可以分享吗?欢迎大家提出意见。

以上是Go 中的生成器并发模式:综合指南的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板