多个goroutine等待共享的定时器导致竞争。

huangapple go评论76阅读模式
英文:

Multiple go routines waiting for a shared Timer causing race

问题

我有一个要求,需要在多个Go协程中更新共享对象的计时器。但是最终出现了竞争条件。我不能在通道上使用锁进行等待,因为所有其他协程都必须等待。

package main
import(
    "time"
    "math/rand"
)
type R struct {
    timer *time.Timer
    //其他字段
}

func f(done chan bool, r *R){
    r.timer = time.NewTimer(time.Millisecond * time.Duration(1000 + rand.Intn(2)))
    //同时访问共享对象r的其他字段的一些代码,不能在这里加锁
    <-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
    }
}

当我使用以下命令运行时:

go run -race thread.go

它会给出以下警告并且挂起:

==================
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
==================

希望能提供帮助。

英文:

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(	
    &quot;time&quot;
	&quot;math/rand&quot;
)
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
	&lt;-r.timer.C

	done &lt;- true
}

func main(){
    done := make(chan bool , 5)
	var r *R
    var t *time.Timer
	r = &amp;R{timer:t}
	for i:=0;i&lt;5;i++{
		go f(done,r)
	}
	for i:=0;i&lt;5;i++{
		&lt;-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

答案1

得分: 3

这里存在一个设计问题 - 你有一个 R 对象,并且它有一个共享实例,但是每个 goroutine 都创建了一个新的本地计时器。在我看来,你需要每个 goroutine 都有一个本地计时器,而不是在它们之间共享一个计时器,这样做没有意义。

如果你按照以下方式重写你的代码:

type R struct {
    //其他字段
    Foo string
    Bar interface{}
}

func f(done chan bool, r *R) {
    timer := time.NewTimer(time.Millisecond * time.Duration(1000+rand.Intn(2)))
    //同时访问共享对象 r 的其他字段的一些代码,不能在这里加锁
    <-timer.C

    done <- true
}

计时器就变成了每个 goroutine 的本地变量,这样你就没有竞争条件,至少对于计时器的访问来说是如此。

请注意,仍然需要通过互斥锁来保护对共享对象的其他字段的每次访问,否则你会遇到相同的问题。

英文:

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
	&lt;-timer.C

	done &lt;- 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.

huangapple
  • 本文由 发表于 2015年4月21日 16:22:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/29766680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定