2016-06-27 2 views
5
type driver struct { 
    variables map[string]string 
} 

var Drivers []driver 

func main() { 

    driver := driver{ 
     variables: make(map[string]string), 
    } 
    Drivers = append(Drivers, driver) 

    driver.variables = make(map[string]string) // Commenting this line makes it work, too 

    done := make(chan bool) 
    go driver.populate(done) 

    <-done 

    fmt.Print(Drivers[0].variables) 
} 

func (this *driver) populate(done chan bool) { 
    time.Sleep(500 * time.Millisecond) 
    this.variables["a"] = "b" 
    done <- true 
} 

я ожидал:Почему эта карта пуста, когда я заполняю ее в Goroutine?

map[a:b] 

Фактический результат:

map[] 

Playground

ответ

5

Проблема проста: у вас есть кусочек driver с:

var Drivers []driver 

Обратите внимание, что Drivers - это фрагмент некоторого типа структуры, а не фрагмент указателей!

Когда вы добавляете что-то (или присвоить значение одного из его элементов):

Drivers = append(Drivers, driver) 

Это делает копию значения приложенном (или назначенный)! Так что, когда вы сделаете это позже:

driver.variables = make(map[string]string) 

Он будет устанавливать новое значение карты в driver.variables, но отличается от значения, хранящегося в Drivers (точнее в Drivers[0]).

Позже вы заполните driver.variables, но вы распечатаете Drivers[0].variables. Они представляют собой две разные значения структуры, с двумя разными значениями карты. Goroutines не играют здесь роли (они должным образом синхронизированы, поэтому они не должны в любом случае).

ли вы напечатать driver.variables:

fmt.Print(driver.variables) 

Вы бы видели (попробуйте на Go Playground):

map[a:b] 

Если закомментировать эту строку:

driver.variables = make(map[string]string) // Commenting this line makes it work, too 

Это будет работать, , но только потому, что, хотя у вас есть 2 значения структуры, они имеют одинаковое значение карты (тот же заголовок карты, указывающий на одну и ту же структуру данных карты).

Вы также можете заставить его работать, если вы звоните driver.populate() по значению STRUCT Drivers[0] (и прилипание к печати Drivers[0].variables):

go Drivers[0].populate(done) 

// ... 

fmt.Print(Drivers[0].variables) 

Попробуйте это на Go Playground.

И вы также можете заставить его работать, если Drivers является срез указателей:

var Drivers []*driver 

// ... 

driver := &driver{ 
    variables: make(map[string]string), 
} 

Поскольку driver и Driver[0] будет такой же указатель (вы будете иметь только одно значение структуры и одно значение карты в качестве начального карта больше недоступна). Попробуйте это на Go Playground.

+4

Правильный ответ: goroutines в этом случае не имеют значения: https://play.golang.org/p/Eh3gPbFWPL показывает проблему с только главной горутином; понимая, что struct _copied_, но указатели (и ссылочные типы, такие как карта) являются общими, что вызывает здесь проблему. – val

1

Причина, почему использование версии goroutine вы не получаете неинициализированный карты является то, что, когда основная функция возвращается, программа выходит: не ждет, пока другие (не основные) goroutines завершатся. Имейте в виду, что главная функция - это goroutine.

Таким образом, даже если вы инициализировать карту с помощью:

driver.variables = make(map[string]string) 

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

Типы карт являются ссылочными типами, такими как указатели или фрагменты, и поэтому значение m выше nil; он не указывает на инициализированную карту. Карта nil ведет себя как пустая карта при чтении, но попытки записать на карту nil вызовут панику во время выполнения; не делайте этого. Для инициализации карты используйте встроенную функцию make.

В случае, если вы удалите ключевое слово go, оно инициализирует карту driver.variables. Но поскольку он работает в одном потоке (основной поток), и вы положили временную задержку на функцию populate, сначала она инициализирует карту, а затем заполняет ее.

+0

я не понимаю. Не выполняется ли засечка() goroutine, пока главный горутин делает время. Спящий (1000 мс)? – AndreKR

+0

После предложения Петра я обновил свой вопрос. Я думаю, что ваше объяснение больше не применяется, но поведение по-прежнему остается прежним. – AndreKR

+0

Да, он работает, но 'driver.variables = make (map [string] string)' будет повторно инициализироваться, если вы вызовете функцию 'populate' на другой goroutine. –

0

Я лучше использовать каналы вместо sleep S:

package main 

import (
    "fmt" 
    "time" 
) 

type driver struct { 
    variables map[string]string 
} 

var Drivers []driver 

func main() { 
    driver := driver{ 
     variables: make(map[string]string), 
    } 
    Drivers = append(Drivers, driver) 

    done := make(chan bool) 
    go driver.populate(done) 
    <-done // wait for goroutine to complete 

    fmt.Print(Drivers[0].variables) 
} 

func (this *driver) populate(done chan bool) { 
    time.Sleep(500 * time.Millisecond) 
    this.variables["a"] = "b" 
    done <- true 
} 

Playground

+0

Отличная идея. Ваша версия все еще показывает ту же проблему и устраняет все сомнения относительно условий гонки. Я обновил свой вопрос. – AndreKR

+0

@AndreKR да, действительно, я только что заметил, что это не связано с goroutines. Вы используете фрагмент структур, а не кусок указателей. Он работает с кусочком указателей: https://play.golang.org/p/R8Vkwh_J9H –

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