Go例程矩阵乘法错误

Go routines Matrix multiplication error

我正在使用 go 例程并行乘以矩阵。我得到的索引超出范围,但是当我 运行 相同的代码顺序时它起作用了。 (按顺序我的意思是评论走线)。我正在使用延迟,所以我不必等待我的例程结束,因为这将是最后一个调用

错误 D:\0000>去 运行 Ap.go 恐慌:运行时间错误:索引超出范围

goroutine 5 [running]:
main.pmultiply(0xc04206c000, 0x3, 0x3, 0xc04206c050, 0x3, 0x3, 0x1, 0x3, 0x0)
        D:/0000/Ap.go:48 +0x95
main.multiply.func1(0xc04206c0a0, 0x3, 0x3, 0xc04200e090, 0xc04200e098, 0xc04206
c000, 0x3, 0x3, 0xc04206c050, 0x3, ...)
        D:/0000/Ap.go:64 +0x94
created by main.multiply
        D:/0000/Ap.go:63 +0x1d7
exit status 2

代码

package main

import "fmt"

func main(){

    matrix_a := make([][]int,3);

    for i:=0;i<len(matrix_a);i++{

        matrix_a[i]=make([]int,3);

    }


    for i:=0;i<len(matrix_a);i++{

        for j:=0;j<len(matrix_a[0]);j++{
            matrix_a[i][j] = 2;
        }
    }

    matrix_b := make([][]int,3);

    for i:=0;i<len(matrix_b);i++{

        matrix_b[i]=make([]int,3);

    }


    for i:=0;i<len(matrix_b);i++{

        for j:=0;j<len(matrix_b[0]);j++{
            matrix_b[i][j] = 2;
        }
    }

    defer fmt.Println(multiply(matrix_a,matrix_b));

}

func pmultiply(matrix_a [][] int,matrix_b [][] int,row int,col int) int{

    sum := 0;

    for z:=0;z<len(matrix_a[0]);z++{
        sum = sum + matrix_a[row][z] *  matrix_b[z][col];
    }
    return sum;
}

func multiply(matrix_a [][] int,matrix_b [][] int) ([][] int){

    matrix_c := make([][]int,3);

    for i:=0;i<len(matrix_c);i++{
        matrix_c[i]=make([]int,3);
    }

    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            go func(){
                matrix_c[i][j] = pmultiply(matrix_a,matrix_b,i,j);
            }()
        }   
    }

    return matrix_c;
}

我看到两个问题:

  1. 您在 multiplyij 中有一个经典的闭包问题。
  2. 无法保证 matrix_c 会在 return 之前计算 multiply

第一个就在这里:

for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        go func(){
            matrix_c[i][j] = pmultiply(matrix_a,matrix_b,i,j);
        }()
    }   
}

当您 go func() { ... }() 时,匿名函数保留对 ij 的引用,而不是 ij 的实际值,所以当goroutine 执行,ij 可以是 0 到 3 之间的任何值( 包含 )。这就是您所知道的错误的来源:ij 是三个,因为 goroutine 在 after 循环完成后执行。最简单的解决方案是强制在正确的时间计算 ij

go func(i, j int) {
    matrix_c[i][j] = pmultiply(matrix_a, matrix_b, i, j)
}(i, j)

第二个问题是 goroutines 不一定会在你之前全部完成 return matrix_c,甚至不能保证它们中的任何一个都会完成。最简单的解决方案是使用 sync.WaitGroup 等待它们完成。首先你会 import "sync",然后调整循环:

var wg sync.WaitGroup
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        wg.Add(1) // Tell the WaitGroup to wait for another thing.
        go func(i, j int) {
            matrix_c[i][j] = pmultiply(matrix_a, matrix_b, i, j)
            wg.Done() // Tell it that we're done.
        }(i, j)
    }
}

然后等待 returning:

wg.Wait()
return matrix_c

对评论的回应: defer 这样不行,the specification only says:

A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

就是这样,它安排在执行离开周围函数时执行某些操作。 defer 与等待 threads/goroutines 没有任何关系,它也不知道延迟函数可能创建的 goroutines。