Подпрограммы Go Ошибка умножения матрицы

Я параллельно умножаю матрицу, используя подпрограммы go. Я получаю индекс за пределами допустимого диапазона, но когда я запускаю один и тот же код последовательно, он работает. (под последовательным я подразумеваю комментирование строки go). Я использую defer, поэтому мне не нужно ждать завершения моих подпрограмм, так как это будет последнее, что вызывается

Ошибка D: \ 0000> иди запустите Ap.go panic: ошибка времени выполнения: индекс вне допустимого диапазона

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;
}

person drainzerrr    schedule 06.05.2018    source источник


Ответы (1)


Я вижу две проблемы:

  1. У вас есть классическая проблема закрытия в multiply с i и j.
  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);
        }()
    }   
}

Анонимная функция хранит ссылку на i и j, а не на фактические значения i и j, когда вы go func() { ... }(), поэтому при выполнении горутины i и j могут быть любыми значениями от нуля до трех (включительно). Вот откуда возникает известная вам ошибка: i или j равно трем, потому что горутина выполняется после завершения циклов. Самое простое решение - заставить i и j оцениваться в нужное время:

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

Вторая проблема заключается в том, что все горутины не обязательно завершатся раньше вас 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 так не работает, в спецификации указано только < / а>:

Оператор defer вызывает функцию, выполнение которой откладывается до момента возврата из окружающей функции, либо потому, что окружающая функция выполнила оператор return, достигла конца своего тела функции, либо потому, что соответствующая горутина паникует.

Вот и все, он организует выполнение чего-либо, когда выполнение покидает окружающую функцию. defer не имеет ничего общего с ожиданием потоков / горутин и ничего не знает о горутинах, которые может создать отложенная функция.

person mu is too short    schedule 06.05.2018
comment
Я думаю, что ваш ответ частично верен. Потому что я использую defer в основном. Defer запустится в конце main, когда все потоки завершатся. поправьте меня, если я ошибаюсь. Но шляпы для выяснения первой части - person drainzerrr; 06.05.2018
comment
@drainzerrr: defer работает не так, пожалуйста, посмотрите мой обновленный ответ. - person mu is too short; 06.05.2018
comment
@IainDuncan Спасибо. Я знаю проблему закрытия из JavaScript и анонимных обработчиков событий внутри циклов. - person mu is too short; 06.05.2018