Гонка данных со списком. Одновременный доступ к списку с мьютексами

У меня гонка за данными, и я не могу понять, почему. Выполняя свои тесты с помощью команды -race, я сузил ее до попытки получить доступ к list.List при чтении из нее, но мои мьютексы, похоже, ничего не делают.

У меня есть ряд * list.Lists внутри массива, например:

type MyList struct {
    mutex sync.Mutex
    *list.List
}

type SomeObj struct {
    data string
}

var myListOfLists [10]MyList

Я читаю и пишу из списка вот так:

list := myListOfLists[someIndex]
list.mutex.Lock()
for e := list.Front(); e != nil; e = e.Next() {
        if (...) {
            list.MoveToFront(e)
        }
}
list.mutex.Unlock()

а в другой горутине также пытается прочитать и построить полный список, чтобы вернуть

var fullCopy []*SomeObj
list := myListOfLists[someIndex]

list.mutex.Lock()
for e := list.Front(); e != nil; e = e.Next() {
        fullCopy = append(fullCopy, e.Value.(SomeObj))
}
list.mutex.Unlock()

person aroooo    schedule 07.12.2018    source источник
comment
Возможно ли, что у вас есть несколько MyList с указателями на один и тот же list.List?   -  person Adrian    schedule 07.12.2018
comment
Действительно ли ваш пример fullCopy []string точен в том смысле, что он собирает строки, или вы, возможно, возвращаете указатели?   -  person RayfenWindspear    schedule 07.12.2018
comment
@RayfenWindspear: да, на самом деле это указатели - моя ошибка. Обновление q   -  person aroooo    schedule 07.12.2018
comment
@Adrian: да, это указатели, но в списке есть мутация, а не объект. обновил q   -  person aroooo    schedule 07.12.2018


Ответы (1)


Оператор list := myListOfLists[someIndex] копирует элемент массива в переменную list. Это копирует мьютекс, предотвращая его работу. Команда go vet сообщает об этой проблеме.

Вы можете избежать копирования, используя указатель на элемент массива:

list := &myListOfLists[someIndex]

Другой подход - использовать массив указателей на MyList. Пока вы это делаете, вы также можете использовать значение списка вместо указателя списка в MyList:

type MyList struct {
    mutex sync.Mutex
    list.List
}

var myListOfLists [10]*MyList
for i := range myListOfLists {
   myListOfLists[i] = &MyList{}
}
person Cerise Limón    schedule 07.12.2018
comment
Чтобы избежать несчастных случаев в будущем, не лучше ли инициализировать как var myListOfLists [10]*MyList? - person RayfenWindspear; 07.12.2018
comment
@RayfenWindspear: да, если у вас есть встроенный мьютекс, вам следует использовать тип указателя (плюс здесь нет причин для массива вместо среза) - person JimB; 07.12.2018
comment
Потрясающе, не знала про ветеринара. Это сработало, спасибо. Есть ли еще какие-нибудь команды go, о которых мне следует знать? Я знаю о тесте и флаге -race, а теперь иду к ветеринару; вот и все. - person aroooo; 07.12.2018