P
P
Pavel2015-12-09 07:49:55
go
Pavel, 2015-12-09 07:49:55

How to solve the problem with DATA RACE?

The essence of the problem is that I have an array of some objects (structures). This structure has some properties and several methods. Bypassing this cycle by timer, I turn to the properties to determine whether it is necessary to call a certain method of this structure in a separate thread.
Here's a code example:

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)
    }
  }
}

When running this code with the "race" parameter (go run -race test.go), I get a warning "WARNING: DATA RACE", namely, it swears at the call to the IsSync method in the loop itself.
The Request method can take up to half a minute, and the array traversal is needed approximately every 2 seconds. I’ll make a reservation right away that the IsSync method is conditional. In reality, there are more complex checks, but it always returns yes or no.
I understand the essence of the problem. That is, the warning in this example is obvious - one day a situation may occur when a thread and a loop can simultaneously access the same property.
But how to fix it? Adding a lock to the IsSync method does not solve the problem.
func (this Loc) IsSync() bool {
  this.mutex.Lock()
  s := this.is_sync
  this.mutex.Unlock()
  return s
}

Answer the question

In order to leave comments, you need to log in

2 answer(s)
U
uvelichitel, 2015-12-09
@uvelichitel

There are two points here

  • go list[i].Request(i)is guaranteed to initiate a thread, but does not necessarily start and this.is_sync = trueis not executed. That is, arbitrarily many goroutines tied to one Loc struct can be initiated. The execution priority of routines in Go is not deterministic.
  • Parameters are passed to the function by copying the value, that is, calling f unc (this Loc) IsSync() boolprovokes a complete copy of Loc, which may be in operation. To avoid copying, pass a link.
Within the framework of your architecture (in order not to change the code much), this should work
package main

import (
  "fmt"
  "sync/atomic"
  "time"
)

// наша структура
type Loc struct {
  is_sync *int32
  Value   int
}

// метод, который говорит, можно ли запускать метод Request
func (this *Loc) IsSync() bool {    //Передавайте параметр ссылкой, что бы избежать копирования
  return (atomic.LoadInt32(this.is_sync) != 0)
}

// основной запрос, который может длиться несколько секунд
func (this *Loc) Request(val int) {

  defer func() {
    atomic.StoreInt32(this.is_sync, 0)
    fmt.Println("End")
  }()

  fmt.Println("Request...")

  this.Value = val
  time.Sleep(time.Second)
}

var list []Loc

func init() {
  list = append(list, Loc{is_sync: new(int32)})
}

func main() {
  c := time.Tick(time.Second * 2)

  for range c {
    for i := 0; i < len(list); i++ {
      if list[i].IsSync() { // если ещё не пришло время, то пропускаем
        continue
      }
      atomic.StoreInt32(list[i].is_sync, 1) //флаг нужно выставить прямо здесь, а не дожидаясь исполнения рутины
      go list[i].Request(i)
    }
  }
}

O
Oleg Shevelev, 2015-12-09
@mantyr

You should look at the approach from the other side. Why do you need a cycle? Make it in every Request() goroutine. One goroutine per Loc object. To control (start/stop between iterations) use a channel.
You will reach the rest yourself, or you will not reach - who knows :)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question