2013-06-14 2 views
3
package main 

import "fmt" 

func main() { 
    completed := make(chan bool, 2) 
    m := map[string]string{"a": "a", "b": "b"} 
    for k, v := range m { 
    go func() { 
     fmt.Println(k, v) 
     completed <- true 
    }() 
    } 
    <- completed 
    <- completed 
} 

Я побежал код сотни раз, выход всегда:Странное поведение goroutine и канала связи

b b 
b b 

Однако, я никогда не видел пару a a распечатаны. Это какая-то странная проблема параллелизма?

ответ

4

Это классический пример "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 - по времени работает анонимная функция, для цикла была закончена, так„к“остается с его последним значением для обоих прогонов функции.

+0

Чтобы быть ясным, он может распечатать первый а, или он может напечатать сначала b. Но он обязательно напечатает обе пары. – MatrixFrog

+0

Спасибо за указание на официальный пример документа, странно, что я никогда не видел «а», но это может быть специфично для этой машины, я думаю. –

0

Согласно the spec, «порядок итераций по картам не указан и не может быть одинаковым с одной итерации на следующую».

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

Если вы хотите перебрать карту ключи в определенном порядке, вы можете указать его самостоятельно, используя кусочек, как описано здесь:

http://blog.golang.org/go-maps-in-action

+1

Спасибо за информацию, я думаю, мне больше любопытно, почему другая карта (a) никогда не появлялась на выходе? –

0

Вы не передаете аргументы горутинцам. Таким образом, они используют одни и те же экземпляры k и v, как и любое их значение, в вашем случае после цикл диапазона завершен. С GOMAXPROCS> 1 у вас также есть гонка данных по этим переменным.

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