Reputation: 463
I have a requirement to update timer of a shared object in multiple go routines. But it end up with race condition. I cannot use lock for waiting on the channel because all the other routines will have to wait.
package main
import(
"time"
"math/rand"
)
type R struct {
timer *time.Timer
//other fields
}
func f(done chan bool,r *R){
r.timer =time.NewTimer(time.Millisecond * time.Duration(1000 + rand.Intn(2)))
//some code simultaneously accessing other fields of shared object r, cannot put a lock here
<-r.timer.C
done <- true
}
func main(){
done := make(chan bool , 5)
var r *R
var t *time.Timer
r = &R{timer:t}
for i:=0;i<5;i++{
go f(done,r)
}
for i:=0;i<5;i++{
<-done
}
}
when I run using
go run -race thread.go
it gives
==================
WARNING: DATA RACE
Write by goroutine 5:
main.f()
usr/local/gocode/thread.go:12 +0x69
Previous write by goroutine 4:
main.f()
usr/local/gocode/thread.go:12 +0x69
Goroutine 5 (running) created at:
main.main()
usr/local/gocode/thread.go:25 +0xd3
Goroutine 4 (running) created at:
main.main()
usr/local/gocode/thread.go:25 +0xd3
==================
and hangs
any help would be useful
Upvotes: 1
Views: 870
Reputation: 49265
There is a design issue here - you have one R object, and it has a shared instance, but every goroutine creates a new timer that is local. Seems to me like you need a local timer per goroutine, and not share that timer between all of them, it just doesn't make sense.
If you rewrite your code like so:
type R struct {
//other fields
Foo string
Bar interface{}
}
func f(done chan bool, r *R) {
timer := time.NewTimer(time.Millisecond * time.Duration(1000+rand.Intn(2)))
//some code simultaneously accessing other fields of shared object r, cannot put a lock here
<-timer.C
done <- true
}
the timer becomes local to the goroutine as it should be, and you have no race condition, at least for the timer access.
Note that still, ever access to the shared object's other fields must be protected by a mutex or you'll get the same issue.
Upvotes: 3