Голанг, состояние гонки с местной картой

Кажется, я не совсем понимаю карты в Go.

У меня есть этот код:

fetch := map[string]int{some data}

for condition {
    fetchlocal := map[string]int{}

    for key, value := range fetch {
        if condition {
            fetchlocal[key] = value
        }
    }

    go threadfunc (fetchlocal)
}

Теперь везде, где функция threadfunc использует переменную fetchlocal Go (go -race), выдается предупреждение: гонка данных. У меня тоже уже несколько паник. Но почему? Переменная fetchlocal не используется никакими другими горутинами.

Может кто-нибудь просветить меня, пожалуйста?


person sh sh sh    schedule 10.04.2014    source источник
comment
На каждой итерации for condition { вы порождаете новую горутину для обработки fetchlocal, таким образом, если ваш threadlocal работает достаточно долго, ваш внутренний цикл range может/изменит ваш map. map не является потокобезопасной структурой данных в Go. Кроме того, это поможет нам ответить, если вы также предоставите threadfunc источник.   -  person Kavu    schedule 10.04.2014
comment
насколько я понимаю, если threadfunc не меняет fetch, гонки быть не должно, но могу ошибаться.   -  person OneOfOne    schedule 10.04.2014
comment
Threadfunc составляет 2000 строк, может быть слишком длинным, чтобы публиковать здесь. Но он просто читает и записывает переменную fetchlocal. Но я думал, что fetchlocal существует только в этом одном разделе цикла, поскольку он создан в нем. Во всяком случае, кто-то опубликовал ответ, чтобы обернуть вызов go в функцию (но, к сожалению, удалил ответ). Но вроде решил проблему. Кроме того, threadfunc работает (читает/записывает) только с fetchlocal, оставляя выборку в покое.   -  person sh sh sh    schedule 10.04.2014
comment
Является ли значение выборки действительно целым числом? Если это изменяемый объект, то его изменение вызовет состояние гонки. Кстати, запуск слегка измененной, но в основном похожей версии вашего кода не вызывает предупреждения о гонке.   -  person Not_a_Golfer    schedule 10.04.2014
comment
Это не меняет его. А в вашем коде вы уверены, что вызываемая функция работает дольше, чем итерация for?   -  person sh sh sh    schedule 10.04.2014
comment
@shshsh вот мой код - он просто доказывает, что экземпляр fetchlocal, передаваемый функции, изменяется при каждом вызове. play.golang.org/p/ykg6Xawpgo   -  person Not_a_Golfer    schedule 10.04.2014
comment
Опубликуйте минимальный пример, для которого сообщается о гонке, где каждая итерация цикла обрабатывает только локальные данные цикла.   -  person Paul Hankin    schedule 10.04.2014
comment
@shshsh Что такое some data выше? Если это данные, которые могут быть изменены theadfunc, тогда будет гонка. См. play.golang.org/p/FWEz-OEfkq.   -  person djd    schedule 15.05.2014
comment
Это может быть ошибка в детекторе гонок. Попробуйте сообщить об ошибке на странице code.google.com/p/go/issues.   -  person fuz    schedule 09.06.2014
comment
Существует гонка между чтением в threadfunc и записью в fetchlocal[key] = value   -  person frank.lin    schedule 05.08.2014


Ответы (1)


Я предполагаю, что ваш fetch := map[string]int{some data} на самом деле должен был быть: fetch := map[string][]int{..some data..}.

Чтобы это была гонка, threadfunc должно изменить значение в пределах fetchlocal или что-то еще должно изменить значение в пределах fetch.

Это означает, что срез на самом деле:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

Когда вы копируете элементы с одной карты на другую, вы не делаете глубокую копию срезов (вы просто создаете новую структуру с теми же данными, длиной, колпачком), то есть так сказать fetch["foo"].Data == fetchlocal["foo"].Data.

Следовательно, вы можете сказать fetch[someExistingKey] = someNewValue, и это не будет гонкой с threadfunc, но если вы скажете fetch[someExistingKey][x] == foobar или fetchlocal[someExistingKey][x] == foobar, гонка начнется.

Если fetchlocal нужно изменить на threadfunc, вы можете изменить свой внутренний цикл, чтобы он выглядел так:

for key, value := range fetch {
    if condition {
        newVal := make([]int, len(value))
        copy(newVal, val)
        fetchlocal[key] = newVal
    }
}

Или, альтернативно, сделайте копию внутри threadfunc по мере необходимости перед мутацией.

P.S. Если вы поделились своим фактическим threadfunc или кодом, изменяющим fetch во время работы этих двух циклов, мы сможем помочь больше.

person voidlogic    schedule 21.06.2014