Проблема возникла во время параллельных вычислений в матрице в голанге

#channel #parallels

Вопрос:

Я столкнулся с ошибкой, когда пытался добавить фиксированное значение к каждому элементу матрицы в моем игрушечном примере. Моя мысль состояла в том, чтобы разделить всю матрицу на несколько подматриц и использовать карту для хранения начального индекса и подматрицы во время параллельных вычислений. Я уже выполнил это предыдущее требование. Но когда я хотел бы пойти дальше, я хотел бы, чтобы мои границы взаимодействовали друг с другом и в то же время не теряли никакой полезной информации, как в играх с клеточными автоматами, их положение определялось бы некоторыми соседями. Поэтому вместо того, чтобы идеально разделять всю матрицу, я намеренно добавляю несколько избыточных строк, чтобы границы могли получить правильное значение, как показано ниже, и в заключительной части комбинации я мог бы просто оставить эти неправильные границы и сохранить правильное вычисленное тело.

введите описание изображения здесь

Предположим, у нас есть три процессора, а входная матрица представляет собой матрицу 10 на 10. Поэтому я бы создал три процедуры go. Как предлагалось ранее, я разрешаю некоторым помещениям обрабатывать пограничную ситуацию. Поэтому я выделил некоторое пространство или перекрытие между различными подразделениями во время сегментации. Для достижения этой цели я вручную добавляю одну строку перед подплатей и одну строку после подплати, что означает, что я должен написать три утверждения if, первая ситуация-подплата начинается с 0, мы могли бы добавить только одну строку из следующей подплати. Вторая ситуация была бы средней, добавьте одну строку сверху и одну строку снизу снизу. Последняя ситуация состояла бы в том, чтобы не добавлять еще одну строку снизу. Однако, когда я пытаюсь запустить код параллельно, кажется, что во время горутины произошло некоторое загрязнение, и я понятия не имел, как это произойдет. Предположительно, входная подплата перед подпрограммой goroutine и подплата в подпрограмме go должны быть одинаковыми, хотя порядок может отличаться. В этом случае значение в функции goroutine может быть загрязнено. Я понятия не имел, почему может возникнуть такая ситуация. Поскольку метод печати и метод точки останова могут не работать в параллельном программировании. Я действительно в замешательстве и понятия не имею, что происходит в этом случае. Я также приложил приведенный ниже код, если вы хотите его скопировать.

  func TestParallelMatrixMapInteraction(t *testing.T) {  wg := sync.WaitGroup{}  boards := GenerateMatrix(10)  //for i := 0; i lt; len(boards); i   {  // fmt.Println(boards[i])  //}  numProcess := 3  nRows := len(boards)   subRows := nRows / numProcess  sumRow := func(subBoard [][]int, c chan map[int][][]int, startIdx int) {  // input  fmt.Println("The input of subBoard in goroutine: ", subBoard, "with start idx ",startIdx)  defer wg.Done()  for i := 0; i lt; len(subBoard); i   {  for j := 0; j lt; len(subBoard[i]); j   {  subBoard[i][j]  = 4  //fmt.Println(subBoard[i][j])  }  }  subBoardMap := make(map[int][][]int)  subBoardMap[startIdx] = subBoard  //fmt.Println("After the modification, the subBoard is:", subBoardMap)  c lt;- subBoardMap  }   c := make(chan map[int][][]int)  //c := make(chan map[int][][]int, subRows)   // iterate through all the available number of process  for i := 0; i lt; numProcess; i   {  // split into numProcess approx . equal pieces  startIdx := i * subRows  endIdx := (i   1) * subRows  if startIdx ==0 {  wg.Add(1)  subBoard := boards[startIdx : endIdx 1]  fmt.Println("The input of subBoard: ", subBoard, "with start idx ",startIdx)  go sumRow(subBoard, c, startIdx)  } else if i lt; numProcess-1 {  wg.Add(1)  subBoard := boards[startIdx-1 : endIdx 1]  fmt.Println("The input of subBoard: ", subBoard, "with start idx ",startIdx-1)  go sumRow(subBoard, c, startIdx-1)  } else { // i = numProcess -1  wg.Add(1)  subBoard := boards[startIdx-1:]  fmt.Println("The input of subBoard: ", subBoard, "with start idx ",startIdx-1)  go sumRow(subBoard, c, startIdx-1)  }  }   newBoard := GenerateEmptyMatrix(10)   for i := 0; i lt; numProcess; i   {  subBoardMap := lt;-c  //fmt.Println("=====================================")  //fmt.Println("The output from channel: It would be a map",subBoardMap)  var startIdx int  var subBoard [][]int  for idx, value := range subBoardMap {  startIdx = idx  subBoard = value  }  endIdx := startIdx   len(subBoard)   var actualStart int  var actualEnd int  if startIdx == 0 {  actualStart = startIdx  actualEnd = actualEnd - 1  } else if endIdx == len(newBoard) {  actualStart = startIdx   1  actualEnd = endIdx  } else {  actualStart = startIdx   1  actualEnd = endIdx - 1  }   for j := actualStart; j lt; actualEnd; j   {  newBoard[j] = subBoard[j-actualStart]  }  }  wg.Wait()   fmt.Println("=================ready to draw the matrix====================")   for i := 0; i lt; len(newBoard); i   {  fmt.Println(newBoard[i])  }  }  func GenerateMatrix(size int) [][]int {  board := make([][]int, size)  for i := 0; i lt; size; i   {  board[i] = make([]int, size)  }   for i := 0; i lt; len(board); i   {  for j := 0; j lt; len(board[0]); j   {  board[i][j] = i  }  }  return board }  func GenerateEmptyMatrix(size int) [][]int {  board := make([][]int, size)  for i := 0; i lt; size; i   {  board[i] = make([]int, size)  }  return board }  

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

 func TestParallelMatrixMap(t *testing.T) {  wg := sync.WaitGroup{}  boards := GenerateMatrix(20)  for i := 0; i lt; len(boards); i   {  fmt.Println(boards[i])  }  numProcess := 3  nRows := len(boards)   subRows := nRows / numProcess  sumRow := func(subBoard [][]int, c chan map[int][][]int, startIdx int) {  fmt.Println("The input of subBoard in goroutine: ", subBoard, "with start idx ",startIdx)  defer wg.Done()  for i := 0; i lt; len(subBoard); i   {  for j := 0; j lt; len(subBoard[0]); j   {  subBoard[i][j]  = 4  }  }  subBoardMap := make(map[int][][]int)  subBoardMap[startIdx] = subBoard  c lt;- subBoardMap  }   c := make(chan map[int][][]int, subRows)   // iterate through all the available number of process  for i := 0; i lt; numProcess; i   {  // split into numProcess approx . equal pieces  startIdx := i * subRows  endIdx := (i   1) * subRows  if i lt; numProcess-1 {  wg.Add(1)  go sumRow(boards[startIdx:endIdx], c, startIdx)  } else { // i = numProcess -1  wg.Add(1)  go sumRow(boards[startIdx:], c, startIdx)  }  }   newBoard := GenerateEmptyMatrix(20)   for i := 0; i lt; numProcess; i   {  subBoardMap := lt;-c  var startIdx int  var subBoard [][]int  for idx, value := range subBoardMap {  startIdx = idx  subBoard = value  }  endIdx := startIdx   len(subBoard)   for j := startIdx; j lt; endIdx; j   {  newBoard[j] = subBoard[j-startIdx]  }  }  wg.Wait()   fmt.Println("=================ready to draw the matrix====================")   for i := 0; i lt; len(newBoard); i   {  fmt.Println(newBoard[i])  }  }  

Ответ №1:

Я нашел ответ сам, обычно это была бы проблема с мелкой копией и глубокой копией в срезе голанга. Поскольку я использовал метод [startIdx-1:endIdx 1] для добавления двух смежных строк, и из-за внутренней природы беспорядка во время параллельных вычислений в goroutine, предыдущие строки могли быть изменены, что объясняет ситуацию, когда мой вывод будет беспорядочным и без порядка.

Чтобы избежать этой проблемы, вы можете создать функцию копирования, которая создаст новый срез, повторит старый и присвоит значение новому. Только таким образом срез, который мы получили, был бы глубокой копией и независимым от других модификаций.