2017-02-10 7 views
3

у меня есть этот упрощенный фрагмент кода (или здесь https://play.golang.org/p/KW8_OHUp9v)мьютекса в цикле приводит к неожиданному выходу

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    mutex := new(sync.Mutex) 

    for i := 1; i < 5; i++ { 
     for j := 1; j < 5; j++ { 
      mutex.Lock() 
      go func() { 
       fmt.Printf("%d + %d = %d\n", i, j, j+i) 
       mutex.Unlock() 
      }() 
     } 
    } 
} 

Он производит выход, как этот

1 + 2 = 3 
1 + 3 = 4 
1 + 4 = 5 
2 + 5 = 7 
2 + 2 = 4 
2 + 3 = 5 
2 + 4 = 6 
3 + 5 = 8 
3 + 2 = 5 
3 + 3 = 6 
3 + 4 = 7 
4 + 5 = 9 
4 + 2 = 6 
4 + 3 = 7 
4 + 4 = 8 

Program exited. 

Глядя на выходе я был удивлен несколько вещей:

  1. Там нет «1 'для j
  2. Есть «5-х для j
  3. Есть только 3 значения для I = 1, вместо 4

Я могу понять отсутствие» 1 'как переменная увеличивается до того, как написано.

Может ли кто-нибудь объяснить 2. и 3.?

ответ

3

Вы закрываете переменные в цикле, а затем запускаете закрытие в отдельном потоке, в то время как эти переменные продолжают меняться. Когда вы это сделаете, ожидайте неожиданного - например, вы видите 5s, потому что j увеличивается на 5 на последней итерации, что приводит к завершению цикла, но j все еще содержит 5, которые затем может прочитать отдельный поток. Это не имеет никакого отношения к вашим мьютексам; это совместное использование переменных. Если вы используете:

go func(i,j int) { 
    fmt.Printf("%d + %d = %d\n", i, j, j+i) 
    mutex.Unlock() 
}(i,j) 

Тогда он будет проходить в значениях i и j в момент запуска вашей goroutine, и последующие итерации не будут влиять на него: https://play.golang.org/p/P3kUP5e1Fp

+0

Обратите внимание, что вам не хватает окончательного '4 + 4', потому что вы не дожидаетесь завершения последнего goroutine. https://play.golang.org/p/lPQWfvAV5S – JimB

+0

Мне было известно, что здесь будут помогать аргументы функции 'i' и' j'. Мне не хватало основы цикла 'for', т.е.эта переменная FIRST увеличивается, а условие THEN проверяется. Это объясняет 5s –

1

При выполнении этого:

go func() { 
       fmt.Printf("%d + %d = %d\n", i, j, j+i) 
       mutex.Unlock() 
      }() 

Текущий горутин делает еще одну петлю, которая увеличивает j.

Шаг по происходит до Printf, поэтому даже думал, что функция была вызвана в то время как
j был < 5 может быть увеличен до 5, прежде чем функция успела распечатать значения.
Другими словами, вы программа работает так:

  • Введите петлю
  • Замок Mutex
  • вызова FUNC()
  • Increment J
  • печати значения
  • Unlock мьютекса

Решение этого вопроса - передать стоимость es по значению для функции вместо того, чтобы делиться ими по всем goroutines.

 Смежные вопросы

  • Нет связанных вопросов^_^