新闻中心
Go语言通道性能探究:缓冲机制真的能提升效率吗?

本文深入探讨go语言中带缓冲与无缓冲通道的性能表现。通过分析实际基准测试案例,揭示了在特定并发模式下,即使期望带缓冲通道能提供性能优势,其效果也可能不明显。文章详细解释了通道同步机制,并强调了在接收方始终准备就绪时,缓冲大小对性能影响的局限性,同时提供了进行可靠性能测试的建议。
Go Channel基础与性能预期
Go语言中的通道(Channel)是实现goroutine之间通信和同步的关键机制。根据其是否带有缓冲区,通道可分为无缓冲通道(Unbuffered Channel)和带缓冲通道(Buffered Channel)。理解两者的工作原理对于编写高效并发代码至关重要:
- 无缓冲通道:实现同步通信。发送操作会阻塞,直到有接收方准备好接收数据;接收操作也会阻塞,直到有发送方发送数据。这意味着发送和接收必须同时发生,形成一个同步点。
- 带缓冲通道:实现异步通信。发送操作只有在通道满时才会阻塞;接收操作只有在通道空时才会阻塞。这允许发送方和接收方在一定程度上解耦,不必严格同步。当通道未满时,发送方可以继续发送而无需等待接收方;当通道未空时,接收方可以继续接收而无需等待发送方。
基于“异步通信”的特性,开发者往往会直观地认为带缓冲通道能够减少发送方或接收方因等待对方而产生的阻塞,从而提升整体性能。这种期望源于缓冲能够平滑数据流,降低同步开销。然而,这种直观的判断并非在所有场景下都成立,其性能优势取决于具体的并发模式和同步需求。
考虑一个常见的并发求和场景:将一个大数组分成多段,每个goroutine负责计算其中一段的和,然后通过通道将结果汇总到主goroutine。在这种模式下,我们可能会预期使用带缓冲通道能减少goroutine之间的同步等待,从而提升整体性能。
千鹿Pr助手
智能Pr插件,融入众多AI功能和海量素材
128
查看详情
实验设计与基准测试示例
为了验证上述性能预期,我们可以设计一个基准测试,比较三种求和方法:
- 线性求和 (linearSum):传统的单线程循环求和,作为性能基线。
- 无缓冲通道求和 (chSum):使用多个goroutine并发计算部分和,并通过无缓冲通道汇总结果。
- 带缓冲通道求和 (chSumBuffer):使用多个goroutine并发计算部分和,并通过带缓冲通道汇总结果。
以下是一个简化的Go语言基准测试代码示例,用于说明这些方法:
package main
import (
"math/rand"
"runtime"
"sync"
"testing"
"time"
)
const arraySize = 10000000 // 数组大小
const numGoroutines = 4 // 工作goroutine数量
// generateRandomArray 生成一个随机整数数组
func generateRandomArray(size int) []int {
arr := make([]int, size)
for i := 0; i < size; i++ {
arr[i] = rand.Intn(100) // 0-99之间的随机数
}
return arr
}
// linearSum 线性求和:单线程执行,作为基准
func linearSum(arr []int) int {
sum := 0
for _, v := range arr {
sum += v
}
return sum
}
// chSum 无缓冲通道求和:使用无缓冲通道汇总部分和
func chSum(arr []int) int {
sum := 0
ch := make(chan int) // 无缓冲通道
var wg sync.WaitGroup
chunkSize := len(arr) / numGoroutines
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go
func(start, end int) {
defer wg.Done()
partialSum := 0
for j := start; j < end; j++ {
partialSum += arr[j]
}
ch <- partialSum // 发送部分和,会阻塞直到有接收方
}(i*chunkSize, (i+1)*chunkSize)
}
// 启动一个goroutine等待所有工作goroutine完成并关闭通道
go func() {
wg.Wait()
close(ch) // 所有发送完成后关闭通道
}()
// 主goroutine从通道接收所有部分和
for partialSum := range ch {
sum += partialSum
}
return sum
}
// chSumBuffer 带缓冲通道求和:使用带缓冲通道汇总部分和
func chSumBuffer(arr []int, bufferSize int) int {
sum := 0
ch := make(chan int, bufferSize) // 带缓冲通道
var wg sync.WaitGroup
chunkSize := len(arr) / numGoroutines
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(start, end int) {
defer wg.Done()
partialSum := 0
for j := start; j < end; j++ {
partialSum += arr[j]
}
ch <- partialSum // 发送部分和,只有通道满时才阻塞
}(i*chunkSize, (i+1)*chunkSize)
}
go func() {
wg.Wait()
close(ch) // 所有发送完成后关闭通道
}()
for partialSum := range ch {
sum += partialSum
}
return sum
}
// 基准测试函数
func BenchmarkLinear(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer() // 重置计时器,排除初始化时间
for i := 0; i < b.N; i++ {
linearSum(arr)
}
}
func BenchmarkChSum(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
chSum(arr)
}
}
func BenchmarkChSumBuffer(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// 缓冲大小可以设置为 numGoroutines,确保每个goroutine至少能发送一次而不阻塞
chSumBuffer(arr, numGoroutines)
}
}
func init() {
// 确保随机数生成器每次运行时不同
rand.Seed(time.Now().UnixNano())
// 设置GOMAXPROCS以上就是Go语言通道性能探究:缓冲机制真的能提升效率吗?的详细内容,更多请关注其它相关文章!
# 完成后
# 商丘网站优化方案
# 康平市场网站建设哪家好
# 网站建设 多少钱
# 包头网站推广网络营销
# 平乡网站建设供应商
# 山东短视频seo公司
# 湖北seo教程平台
# seo岗位激励
# 营销人员怎么推广的
# 奇热影视网站建设ppt
# 模式下
# 也会
# go
# 内存管理
# 是一个
# 单线程
# 多个
# 随机数
# 时才
# red
# 同步机制
# 性能测试
# unix
# ai
# go语言
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
在Socket.IO连接中实现Access Token自动更新与动态重连
汽车之家官方网站官网入口_汽车之家网页版直接进入
淘宝网网页版登录入口 淘宝官方网页版快捷登录
怎样更改Windows系统的默认安装路径_避免C盘爆满的终极设置【技巧】
UE5.7引擎表现爆炸优化无敌!5090跑4K稳定60FPS
护手霜蹭到袖口上了如何清洗? 怎样避免留下一圈油印?
css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染
蛙漫安全无毒 官方认证的绿色入口
飞书妙记怎样用语音转文字速记_飞书妙记用语音转文字速记【速记方法】
海量存储:机器视觉智能化的核心基石
J*a TimerTask文件监控:HashMap状态管理与常见陷阱规避指南
今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程
Win10如何恢复误删的快捷方式_Win10重建常用软件快捷方式
c++ 命名空间怎么用 c++ namespace使用指南
迅雷下载到U盘速度很慢怎么办_迅雷U盘下载慢优化方法
J*aScript中针对特定容器内图片动画的实现教程
Pandas DataFrame 高效批量赋值:告别循环与笛卡尔积误区
如何使用Go和Martini动态服务解码后的图片
微信客户端如何收红包_微信客户端接收红包使用教程
Spyder启动失败:字体文件权限拒绝错误解决方案
Sublime Text怎么设置垂直标尺_Sublime配置Rulers规范代码长度
Win11怎么开启卓越性能模式 Win11电源选项启用高性能释放硬件潜力【方法】
夸克浏览器网页版最新地址 夸克浏览器官方入口合集
在Go开发中优雅管理ListenAndServe进程:GoSublime集成方案
Go语言中JSON数据解码与字段访问指南
cad如何更改注释性对象的比例_cad注释性比例调整方法
uc手机浏览器网页版入口 uc浏览器手机版便捷登录首页
在J*a中如何开发在线活动报名与管理系统_活动报名管理项目实战解析
Angular中单选按钮的正确使用与常见陷阱解析
Yandex搜索引擎官方地址 俄罗斯网络世界的主要入口
C++20的source_location是什么_C++在编译期获取源码位置信息用于日志和断言
Safari怎么安装扩展程序 浏览器插件安装与管理方法【详解】
使用J*aScript检测输入元素是否包含在特定类中
Django模型中自动计算可用余额的实现方法
CSS实现侧边栏导航项全宽圆角悬停背景效果
一加 Nord 5 隐私权限异常_一加 Nord 5 系统安全优化
不同用户不同价格! 索尼开启账户个性化定价测试
顺丰国际快递查询 国际件官方查询入口
如何在J*a中使用Locale处理多语言环境
Lar*el头像管理:图片缩放与旧文件删除的最佳实践
Golang如何实现Web文件静态资源服务器_Golang静态资源服务器开发与实践
qq邮箱发邮件给国外发不出去_QQ邮箱国际邮件发送失败原因与解决
理解J*aScript Promise的微任务队列与执行顺序
Mac怎么锁定备忘录_Mac备忘录加密设置教程
如何使用纯J*aScript判断Input元素是否在特定类容器内
QQ邮箱网页版入口登录 QQ邮箱在线邮箱官方通道
如何修改开机登录密码_Windows账户安全设置超详细教程【必学】
KFC游戏互动怎么赢取优惠券_KFC线上游戏活动参与与优惠代码赢取教程
12306选座怎么选到特殊座位_12306特殊座位选择注意事项
从J*aScript对象中精确提取指定属性的教程


2025-11-12
浏览次数:次
返回列表
func(start, end int) {
defer wg.Done()
partialSum := 0
for j := start; j < end; j++ {
partialSum += arr[j]
}
ch <- partialSum // 发送部分和,会阻塞直到有接收方
}(i*chunkSize, (i+1)*chunkSize)
}
// 启动一个goroutine等待所有工作goroutine完成并关闭通道
go func() {
wg.Wait()
close(ch) // 所有发送完成后关闭通道
}()
// 主goroutine从通道接收所有部分和
for partialSum := range ch {
sum += partialSum
}
return sum
}
// chSumBuffer 带缓冲通道求和:使用带缓冲通道汇总部分和
func chSumBuffer(arr []int, bufferSize int) int {
sum := 0
ch := make(chan int, bufferSize) // 带缓冲通道
var wg sync.WaitGroup
chunkSize := len(arr) / numGoroutines
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(start, end int) {
defer wg.Done()
partialSum := 0
for j := start; j < end; j++ {
partialSum += arr[j]
}
ch <- partialSum // 发送部分和,只有通道满时才阻塞
}(i*chunkSize, (i+1)*chunkSize)
}
go func() {
wg.Wait()
close(ch) // 所有发送完成后关闭通道
}()
for partialSum := range ch {
sum += partialSum
}
return sum
}
// 基准测试函数
func BenchmarkLinear(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer() // 重置计时器,排除初始化时间
for i := 0; i < b.N; i++ {
linearSum(arr)
}
}
func BenchmarkChSum(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
chSum(arr)
}
}
func BenchmarkChSumBuffer(b *testing.B) {
arr := generateRandomArray(arraySize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// 缓冲大小可以设置为 numGoroutines,确保每个goroutine至少能发送一次而不阻塞
chSumBuffer(arr, numGoroutines)
}
}
func init() {
// 确保随机数生成器每次运行时不同
rand.Seed(time.Now().UnixNano())
// 设置GOMAXPROCS