You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go协程实现矩阵乘法时遇索引越界错误求助

解决Go协程并行矩阵乘法的索引越界问题

嘿,我来帮你捋捋这个问题哈!你遇到的并行时索引越界、串行就正常的情况,其实是Go协程使用中两个非常常见的坑——循环变量的引用陷阱,还有主协程没等子协程跑完就提前溜了,而且你用defer来“避免等待协程”的思路完全跑偏了,defer和协程同步半毛钱关系都没有哦。

一、为啥会出问题?

1. 循环变量是“共享引用”的坑

Go的for循环里,循环变量(比如你用来遍历行/列的i、j)是复用同一个内存地址的。如果你直接在循环里启动协程,并且在协程里用这些变量,那所有协程都会盯着同一个变量的地址看。等循环快速跑起来,协程真正开始执行的时候,i、j的值早已经变了——比如循环到最后一次,i变成了矩阵的行数,这时候你用这个值去访问矩阵,可不就索引越界了嘛!

举个直观的错误例子:

rows := 3
for i := 0; i < rows; i++ {
    go func() {
        // 这里的i是引用,大概率拿到的是3,访问matrix[i]直接越界
        fmt.Println(matrix[i])
    }()
}

2. 主协程没等子协程就跑路了

你说用defer来避免等待协程,这完全是误解了defer的作用——defer只是让函数在当前函数退出前延迟执行,它根本不会阻塞主协程等子协程完成。主协程跑完main函数就直接退出了,这时候所有还在干活的子协程会被强制终止,要是终止前刚好在访问矩阵索引,就很容易因为变量值乱掉触发越界。

二、怎么修复?

针对这两个坑,咱们一步一步来解决:

1. 给协程传循环变量的“副本”

启动协程的时候,把当前i、j的值作为参数传给协程函数,这样每个协程拿到的都是独立的副本,不会被后续循环的变量值影响。

比如你的协程调用可以改成这样:

for i := 0; i < aRows; i++ {
    for j := 0; j < bCols; j++ {
        // 把当前的i和j值作为参数传给匿名函数,每个协程拿自己的副本
        go func(i, j int) {
            pmultiply(a, aRows, aCols, b, bRows, bCols, i, j, c)
        }(i, j)
    }
}

这样每个协程手里的i、j都是启动时的正确值,不会出现越界的情况。

2. 用sync.WaitGroup让主协程等一等

要确保主协程等所有子协程都干完活再退出,咱们得用sync.WaitGroup来做同步:

import "sync"

func multiply(a [][]int, b [][]int) [][]int {
    aRows := len(a)
    bCols := len(b[0])
    // 先初始化结果矩阵
    c := make([][]int, aRows)
    for i := range c {
        c[i] = make([]int, bCols)
    }

    var wg sync.WaitGroup
    // 先告诉WaitGroup要等多少个协程
    wg.Add(aRows * bCols)

    for i := 0; i < aRows; i++ {
        for j := 0; j < bCols; j++ {
            go func(i, j int) {
                defer wg.Done() // 协程干完活就告诉WaitGroup我完成了
                pmultiply(a, aRows, aCols, b, bRows, bCols, i, j, c)
            }(i, j)
        }
    }

    wg.Wait() // 主协程在这里阻塞,直到所有协程都调用了Done()
    return c
}

这样主协程就会老老实实地等所有子协程都把矩阵乘法的计算做完,再返回结果,不会提前跑路导致子协程被打断。

3. 检查pmultiply内部的索引逻辑

另外,你也得确认下pmultiply函数里的索引计算对不对——比如矩阵乘法要求a的列数等于b的行数,还有计算c[i][j]时的累加循环,有没有出现超出矩阵维度的情况,这也是可能导致越界的小细节。

三、总结一下

你的问题就是Go协程使用中两个经典陷阱的组合,解决起来也很直接:

  • 给每个协程传循环变量的副本,避免共享引用踩坑
  • 用sync.WaitGroup同步主协程和子协程,确保所有任务完成再退出
  • 核对矩阵维度和内部索引的合法性

内容的提问来源于stack exchange,提问作者drainzerrr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:36:58