0

0

Go 并发处理切片:使用 WaitGroup 实现安全的并行函数调用

花韻仙語

花韻仙語

发布时间:2026-01-06 17:18:08

|

610人浏览过

|

来源于php中文网

原创

Go 并发处理切片:使用 WaitGroup 实现安全的并行函数调用

本文详解如何将顺序遍历切片并调用函数的操作改为真正的并发执行,避免常见闭包捕获错误和切片越界问题,使用 sync.waitgroup 安全收集结果。

在 Go 中实现切片元素的并行处理,核心目标是:对每个输入值独立启动 goroutine 执行计算(如 double(i)),不相互阻塞,最终按原始顺序聚合结果。初学者常误用 channel 切片(如 []chan int)并陷入索引越界或闭包变量捕获陷阱——这正是原代码中 chans[i] = make(chan int) panic 的根源:chans 被声明为零长度切片,却直接通过索引赋值。

更简洁、安全且符合 Go 习惯的做法是:预分配结果切片 + sync.WaitGroup 协调完成信号 + 显式传参避免闭包陷阱。以下是优化后的完整实现:

package main

import (
    "fmt"
    "sync"
    "time"
)

func double(i int) int {
    result := 2 * i
    fmt.Printf("double(%d) = %d\n", i, result)
    time.Sleep(500 * time.Millisecond) // 模拟耗时操作(原代码为 500ms,非 500ns)
    return result
}

func notParallel(arr []int) []int {
    outArr := make([]int, 0, len(arr))
    for _, i := range arr {
        outArr = append(outArr, double(i))
    }
    return outArr
}

func parallel(arr []int) []int {
    n := len(arr)
    outArr := make([]int, n) // 预分配,确保索引安全
    var wg sync.WaitGroup

    for i, num := range arr {
        wg.Add(1)
        // 关键:显式将 i 和 num 作为参数传入 goroutine
        // 避免循环变量被所有 goroutine 共享(闭包陷阱)
        go func(idx int, value int) {
            defer wg.Done()
            outArr[idx] = double(value)
        }(i, num)
    }

    wg.Wait() // 主协程等待所有 goroutine 完成
    return outArr
}

func main() {
    arr := []int{7, 8, 9}
    fmt.Println("=== 顺序执行 ===")
    start := time.Now()
    seq := notParallel(arr)
    fmt.Printf("结果: %v, 耗时: %v\n", seq, time.Since(start))

    fmt.Println("\n=== 并发执行 ===")
    start = time.Now()
    conc := parallel(arr)
    fmt.Printf("结果: %v, 耗时: %v\n", conc, time.Since(start))
}

关键要点说明:

网趣购物系统精装版
网趣购物系统精装版

精装版对原程序进行了大量的更新和调整,在安全性和实用性上均有重大突破,特色功能:完美整合支付宝功能,根据用户需求,并具有打开和关闭支付宝的功能!匿名用户购买功能,商城支持匿名直接购买商品功能,方便用户购物!增加了后台LOGO图片上传管理功能,管理简单、易用对广告管理进行扩充,所有广告图片、FLASH均可实现在线上传管理!多种在线支付方式,程序同时支持网银、西部支付,可自由选择切换!支持简繁互换显示

下载
  • 预分配结果切片:outArr := make([]int, len(arr)) 确保后续 outArr[i] = ... 不会 panic;
  • WaitGroup 精确计数:wg.Add(1) 在 goroutine 启动前调用,defer wg.Done() 保证异常时也能释放;
  • 闭包安全传参:go func(idx, value int) { ... }(i, num) 将当前循环变量值拷贝传入,杜绝 i 和 num 在循环结束后被覆盖导致的竞态;
  • ⚠️ 无需 channel 切片:本场景只需“写入固定位置”,channel 带来额外复杂度(缓冲管理、接收阻塞、关闭逻辑),反而降低可读性与性能;
  • ? 时间验证:3 个元素各耗时 ~500ms,顺序执行约 1500ms,而并发执行接近 500ms(取决于调度开销),效果显著。

若未来需处理不确定数量结果、或需流式消费中间结果,则 channel 方案才具优势——但此时应使用单个 chan T 配合 for range 接收,而非 channel 切片。对于确定长度、顺序敏感的并行映射(map-reduce 中的 map 阶段),WaitGroup + 预分配切片 是最推荐的 Go 模式。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

314

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

528

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

193

2025.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

97

2025.10.23

go语言闭包相关教程大全
go语言闭包相关教程大全

本专题整合了go语言闭包相关数据,阅读专题下面的文章了解更多相关内容。

133

2025.07.29

go语言 数组和切片
go语言 数组和切片

本专题整合了go语言数组和切片的区别与含义,阅读专题下面的文章了解更多详细内容。

45

2025.09.03

PPT动态图表制作教程大全
PPT动态图表制作教程大全

本专题整合了PPT动态图表制作相关教程,阅读专题下面的文章了解更多详细内容。

13

2026.01.07

热门下载

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

精品课程

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

共32课时 | 3.4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

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

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