【问题标题】:Go routines Matrix multiplication errorGoroutines 矩阵乘法错误
【发布时间】:2018-05-06 04:34:10
【问题描述】:

我正在使用 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;
}

【问题讨论】:

    标签: go parallel-processing goroutine indexoutofrangeexception


    【解决方案1】:

    我看到两个问题:

    1. multiply 中有一个经典的闭包问题,ij
    2. 无法保证在您将 matrix_c 返回到 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);
            }()
        }   
    }
    

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

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

    第二个问题是 goroutine 不一定会在你 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)
        }
    }
    

    然后等待返回:

    wg.Wait()
    return matrix_c
    

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

    “defer”语句调用一个函数,该函数的执行被延迟到周围函数返回的那一刻,要么是因为周围函数执行了一个 return 语句,到达其函数体的末尾,要么是因为相应的 goroutine 正在恐慌。

    就是这样,当执行离开周围的函数时,它安排了一些要执行的东西。 defer 与等待线程/goroutine 没有任何关系,也不知道延迟函数可能创建的 goroutine。

    【讨论】:

    • 不错的答案!我喜欢这里对 ​​goroutine in loop 问题的解释:github.com/golang/go/wiki/…
    • 我认为您的回答部分正确。因为我在 main 中使用 defer。当所有线程都完成后,Defer 将在 main 结束时运行。如果我错了,请纠正我。但是要弄清楚第一部分的帽子
    • @drainzerrr:defer 不是这样工作的,请参阅我的更新答案。
    • @IainDuncan 谢谢。我知道循环内的 JavaScript 和匿名事件处理程序的闭包问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    • 2016-03-27
    • 2015-11-19
    相关资源
    最近更新 更多