0

0

Golang并发:缓冲通道为何有时比非缓冲通道慢?

聖光之護

聖光之護

发布时间:2025-10-11 08:28:31

|

744人浏览过

|

来源于php中文网

原创

golang并发:缓冲通道为何有时比非缓冲通道慢?

本文旨在解释在 Golang 并发编程中,为何使用缓冲通道(buffered channel)有时反而比非缓冲通道(unbuffered channel)更慢。我们将通过示例代码分析,深入探讨缓冲通道的初始化开销以及它对程序性能的影响,并提供优化建议。

在 Golang 中,通道(channel)是 goroutine 之间进行通信和同步的重要机制。通道分为缓冲通道和非缓冲通道两种类型。直观上,缓冲通道似乎应该比非缓冲通道更高效,因为它允许发送者在通道未满时继续发送数据,而无需立即等待接收者。然而,在某些情况下,缓冲通道的性能反而不如非缓冲通道。这通常与缓冲通道的初始化开销有关。

让我们通过一个具体的例子来理解这个问题。以下代码片段展示了一个使用缓冲通道和非缓冲通道的 HTML 文本提取程序:

package main

import (
    "fmt"
    "math/rand"
    "os"
    "sync"
    "time"

    sel "code.google.com/p/go-html-transform/css/selector"
    h5 "code.google.com/p/go-html-transform/h5"
    gnhtml "code.google.com/p/go.net/html"
)

// Find a specific HTML element and return its textual element children.
func main() {
    test := `

  
    This is the test document!
    
  
  
    
This is some text
` // Get a parse tree for this HTML h5tree, err := h5.NewFromString(test) if err != nil { die(err) } n := h5tree.Top() // Create a Chain object from a CSS selector statement chn, err := sel.Selector("#h") if err != nil { die(err) } // Find the item. Should be a div node with the text "This is some text" h := chn.Find(n)[0] // run our little experiment this many times total var iter int = 100000 // When buffering, how large shall the buffer be? var bufSize uint = 100 // Keep a running total of the number of times we've tried buffered // and unbuffered channels. var bufCount int = 0 var unbufCount int = 0 // Keep a running total of the number of nanoseconds that have gone by. var bufSum int64 = 0 var unbufSum int64 = 0 // Call the function {iter} times, randomly choosing whether to use a // buffered or unbuffered channel. for i := 0; i < iter; i++ { if rand.Float32() < 0.5 { // No buffering unbufCount += 1 startTime := time.Now() getAllText(h, 0) unbufSum += time.Since(startTime).Nanoseconds() } else { // Use buffering bufCount += 1 startTime := time.Now() getAllText(h, bufSize) bufSum += time.Since(startTime).Nanoseconds() } } unbufAvg := unbufSum / int64(unbufCount) bufAvg := bufSum / int64(bufCount) fmt.Printf("Unbuffered average time (ns): %v\n", unbufAvg) fmt.Printf("Buffered average time (ns): %v\n", bufAvg) } // Kill the program and report the error func die(err error) { fmt.Printf("Terminating: %v\n", err.Error()) os.Exit(1) } // Walk through all of a nodes children and construct a string consisting // of c.Data where c.Type == TextNode func getAllText(n *gnhtml.Node, bufSize uint) string { var texts chan string if bufSize == 0 { // unbuffered, synchronous texts = make(chan string) } else { // buffered, asynchronous texts = make(chan string, bufSize) } wg := sync.WaitGroup{} // Go walk through all n's child nodes, sending only textual data // over the texts channel. wg.Add(1) nTree := h5.NewTree(n) go func() { nTree.Walk(func(c *gnhtml.Node) { if c.Type == gnhtml.TextNode { texts <- c.Data } }) close(texts) wg.Done() }() // As text data comes in over the texts channel, build up finalString wg.Add(1) finalString := "" go func() { for t := range texts { finalString += t } wg.Done() }() // Return finalString once both of the goroutines have finished. wg.Wait() return finalString }

在这个例子中,getAllText 函数使用 goroutine 和 channel 来提取 HTML 节点中的文本。它接受一个 bufSize 参数,用于指定通道的缓冲区大小。如果 bufSize 为 0,则使用非缓冲通道;否则,使用具有指定缓冲区大小的缓冲通道。

立即学习go语言免费学习笔记(深入)”;

最初的实验结果表明,使用缓冲区大小为 100 的缓冲通道的平均运行时间明显高于非缓冲通道。这似乎违反了直觉。

原因分析

关键在于缓冲通道的初始化开销。当创建一个缓冲通道时,Go 运行时需要分配一块内存来存储通道中的元素。缓冲区越大,分配的内存就越多。在上述例子中,每次调用 getAllText 函数时,都会创建一个新的缓冲通道。如果缓冲区大小设置得过大,频繁的内存分配和回收可能会导致性能下降。

Replit Ghostwrite
Replit Ghostwrite

一种基于 ML 的工具,可提供代码完成、生成、转换和编辑器内搜索功能。

下载

优化方案

为了验证这个假设,我们将缓冲区大小从 100 减小到 10。再次运行程序,得到的结果如下:

Buffered average time (ns): 21930
Buffered average time (ns): 22721
Buffered average time (ns): 23011
Buffered average time (ns): 23707
Buffered average time (ns): 27701
Buffered average time (ns): 28325
Buffered average time (ns): 28851
Buffered average time (ns): 29641
Buffered average time (ns): 30417
Buffered average time (ns): 32600
Unbuffered average time (ns): 21077
Unbuffered average time (ns): 21490
Unbuffered average time (ns): 22332
Unbuffered average time (ns): 22584
Unbuffered average time (ns): 26438
Unbuffered average time (ns): 26824
Unbuffered average time (ns): 27322
Unbuffered average time (ns): 27926
Unbuffered average time (ns): 27985
Unbuffered average time (ns): 30322

可以看到,使用缓冲区大小为 10 的缓冲通道的平均运行时间与非缓冲通道的平均运行时间非常接近。这表明,减小缓冲区大小可以有效地降低初始化开销,从而提高程序性能。

总结与建议

  • 在 Golang 并发编程中,缓冲通道并非总是比非缓冲通道更高效。
  • 缓冲通道的初始化开销可能会对程序性能产生负面影响,尤其是在频繁创建和销毁通道的情况下。
  • 选择合适的缓冲区大小非常重要。过大的缓冲区可能会导致内存分配和回收的开销增加,而过小的缓冲区则可能导致通道阻塞。
  • 在实际应用中,应根据具体情况选择合适的通道类型和缓冲区大小。如果通道的创建和销毁频率很高,并且传输的数据量较小,则可以考虑使用非缓冲通道或较小的缓冲通道。

此外,还可以考虑以下优化策略:

  1. 重用通道: 避免频繁创建和销毁通道。如果可能,可以重用已有的通道,以减少内存分配和回收的开销。
  2. 使用 sync.Pool: 可以使用 sync.Pool 来管理缓冲通道,从而避免频繁的内存分配。
  3. 分析程序瓶颈: 使用性能分析工具(如 pprof)来识别程序中的性能瓶颈,并针对性地进行优化。

通过深入理解缓冲通道的特性和潜在的性能问题,我们可以编写出更高效、更可靠的 Golang 并发程序。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

180

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

340

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

209

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

393

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

197

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

191

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

253

2025.06.17

菜鸟裹裹入口以及教程汇总
菜鸟裹裹入口以及教程汇总

本专题整合了菜鸟裹裹入口地址及教程分享,阅读专题下面的文章了解更多详细内容。

0

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

CSS教程
CSS教程

共754课时 | 22.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号