Это классический пример "Race on counter loop". Если вы запустите свой код с go run -race
, я подозреваю, что он вам это скажет.
Ниже будет делать то, что вы ожидаете:
func main() {
completed := make(chan bool, 2)
m := map[string]string{"a": "a", "b": "b"}
for k, v := range m {
go func(k, v string) {
fmt.Println(k, v)
completed <- true
}(k, v)
}
<- completed
<- completed
}
Ваш исходный код может печатать только Б (или только-х), на любой машине, а на самом деле это происходит на детской площадке Go: http://play.golang.org/p/Orgn030Yfr
Это происходит потому, что анонимная функция имеет в виду переменных от for k, v
линии, а не значения, что эти переменные случаются иметь в данный момент создается goroutine. Сначала обе переменные устанавливаются в одно значение, а один goroutine порождается, тогда они устанавливаются на другое значение, а другой goroutine порождается. Затем оба gotoutines запускаются, и оба они видят самые новые значения k и v. Кстати, это не совсем специфично для многопоточности или для Go (play.golang.org запускает все в одном потоке и все еще показывает это " . ошибка ") Эта же проблема возникает в JavaScript там, где гарантированно будет только одна нить:
obj = {a: 'a', b: 'b'};
for (k in obj) {
setTimeout(function() { console.log(k, obj[k]); }, 0);
}
http://goo.gl/vwrMQ - по времени работает анонимная функция, для цикла была закончена, так„к“остается с его последним значением для обоих прогонов функции.
Чтобы быть ясным, он может распечатать первый а, или он может напечатать сначала b. Но он обязательно напечатает обе пары. – MatrixFrog
Спасибо за указание на официальный пример документа, странно, что я никогда не видел «а», но это может быть специфично для этой машины, я думаю. –