Суть проблемы в том, что я имею массив каких-то объектов (структур). У этой структуры есть какие-то свойства и несколько методов. Обходя по таймеру этот цикл я обращаюсь к свойствам, чтобы определить, нужно ли вызывать определенный метод этой структуры в отдельном потоке.
Приведу пример кода:
import (
"fmt"
"sync"
"time"
)
// наша структура
type Loc struct {
mutex sync.Mutex
is_sync bool
Value int
}
// метод, который говорит, можно ли запускать метод Request
func (this Loc) IsSync() bool {
return this.is_sync
}
// основной запрос, который может длиться несколько секунд
func (this *Loc) Request(val int) {
this.is_sync = true
defer func() {
this.is_sync = false
fmt.Println("End")
}()
fmt.Println("Request...")
this.Value = val
time.Sleep(time.Second)
}
var list []Loc
func init() {
list = append(list, Loc{})
}
func main() {
c := time.Tick(time.Second * 2)
for range c {
for i := 0; i < len(list); i++ {
if list[i].IsSync() { // если ещё не пришло время, то пропускаем
continue
}
go list[i].Request(i)
}
}
}
При запуске данного кода с параметром "race" (go run -race test.go) получаю предупреждение "WARNING: DATA RACE", а именно ругается на обращение к методу IsSync в самом цикле.
Метод Request может выполняться до полуминуты, а обход массива нужен примерно каждые 2 секунды. Сразу оговорюсь, что метод IsSync условен. В действительности там более сложные проверки, но всегда возвращает да или нет.
Суть проблемы понимаю. То есть предупреждение в данном примере очевидно - в одно прекрасное время может произойти такая ситуация, когда поток и цикл могут одновременно обратиться к одному и тому же свойству.
Но как это исправить? Если добавить блокировку в метод IsSync, это не решит проблему.
func (this Loc) IsSync() bool {
this.mutex.Lock()
s := this.is_sync
this.mutex.Unlock()
return s
}