2015-08-19 3 views
4

Я пытаюсь понять каналы в Go. Вот пример кода:Go channels: почему два разных выхода?

package main 

import "fmt" 

func main() { 
    m := make(map[int]string) 
    m[2] = "First Value" 
    c := make(chan bool) 
    go func() { 
     m[2] = "Second Value" 
     c <- true 
    }() 
    fmt.Printf("1-%s\n", m[2]) 
    fmt.Printf("2-%s\n", m[2]) 
    _ = <-c 
    fmt.Printf("3-%s\n", m[2]) 
    fmt.Printf("4-%s\n", m[2]) 
} 

Иногда выход кода выше был (результат 1):

1-First Value 
2-First Value 
3-Second Value 
4-Second Value 

но иногда я получил (результат 2):

1-First Value 
2-Second Value 
3-Second Value 
4-Second Value 

После изменение c := make(chan bool) до c := make(chan bool, 1), то же самое произошло: иногда результат 1, иногда результат 2.

Почему?

+4

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

ответ

4

Ваши результаты имеют смысл. Поскольку go run run независимо друг от друга, вы никогда не узнаете, когда начнется обычная процедура. Как только строка

m[2] = "Second Value" 

Выполнено, это отразится на вашей обычной рутине.

Поэтому, когда выше линии выполняются между первой и второй печатью вашей программы вы видите результат как

1-First Value 
2-Second Value 
3-Second Value 
4-Second Value 

Когда это вы не видите другую. Перед третьей печатью вы убедитесь, что другая процедура go завершена.

Просто, чтобы очистить его еще больше, если вы измените вашу программу немного, как

package main 

import "fmt" 
import "time" 

func main() { 
    m := make(map[int]string) 
    m[2] = "First Value" 
    c := make(chan bool) 
    go func() { 
     m[2] = "Second Value" 
     c <- true 
    }() 
    time.Sleep(time.Second) 
    fmt.Printf("1-%s\n", m[2]) 
    fmt.Printf("2-%s\n", m[2]) 
    _ = <-c 
    fmt.Printf("3-%s\n", m[2]) 
    fmt.Printf("4-%s\n", m[2]) 
} 

Playground

Вы, скорее всего, получите следующий результат

1-Second Value 
2-Second Value 
3-Second Value 
4-Second Value 

Надеется, что это помогает.

2

Вы фактически кодируете только то, что третий и четвертый отпечатки отображают второе значение. Когда вы меняете канал на буфер 1, он ничего не меняет.

Итак, давайте посмотрим на первый случай. У вас есть небуферизованный канал. При запуске рутины вы меняете содержимое карты. Но вы не знаете, когда планировщик его запустит. Вот почему иногда вы видите один результат, а другой - другой результат. Звонок: _ = <-c будет гарантировать, что рутинная работа будет запущена. Поскольку этот вызов будет блокироваться до тех пор, пока обычная программа на самом деле ничего не напишет на канале. Вот почему вы никогда не увидите «Первое значение» в последних двух отпечатках.

Когда вы используете буферный канал, единственное, что изменится, это то, что процедура go немедленно выйдет после записи на канал. Больше ничего (читайте по адресу Effective Go).

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