为什么这段代码会产生DATA RACE呢?
```go
// main.go
package main
import (
"fmt"
"log"
"sync"
"time"
)
type runner struct {
mutableCh chan func()
close chan struct{}
}
var pool = &sync.Pool{
New: func() interface{} {
return new(runner)
},
}
func newRunner() *runner {
r := pool.Get().(*runner)
r.mutableCh = make(chan func())
r.close = make(chan struct{})
return r
}
func (r *runner) send(fn func()) error {
wait := make(chan struct{})
var err error
r.mutableCh <- func() {
defer func() {
if e := recover(); e != nil {
log.Printf("%s", e)
err = fmt.Errorf("error: %s", e)
close(wait)
}
}()
fn()
close(wait)
}
<-wait
return err
}
func (r *runner) start() {
go func() {
for {
select {
case fn := <-r.mutableCh:
fn()
case <-r.close:
return
}
}
}()
}
func (r *runner) stop() {
close(r.close)
}
func releaserunner(s *runner) {
s.mutableCh = nil
s.close = nil
pool.Put(s)
}
func main() {
r := newRunner()
defer releaserunner(r)
r.start()
// err1 := r.send(func() {
// fmt.Println("send---")
// })
// fmt.Println(err1)
r.stop()
time.Sleep(time.Second * 1)
}
```
### 执行 go run -race main.go
```
==================
WARNING: DATA RACE
Write at 0x00c00011a220 by main goroutine:
main.releaserunner()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:71 +0x3e
main.main()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:87 +0x1b0
Previous read at 0x00c00011a220 by goroutine 7:
main.(*runner).start.func1()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:53 +0x64
Goroutine 7 (finished) created at:
main.(*runner).start()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:50 +0x4c
main.main()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:79 +0x17e
==================
==================
WARNING: DATA RACE
Write at 0x00c00011a228 by main goroutine:
main.releaserunner()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:72 +0x69
main.main()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:87 +0x1b0
Previous read at 0x00c00011a228 by goroutine 7:
main.(*runner).start.func1()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:55 +0x8a
Goroutine 7 (finished) created at:
main.(*runner).start()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:50 +0x4c
main.main()
/home/users/zhangming12/gopath/src/icode.baidu.com/baidu/ps-se-go/exgraph/test.go:79 +0x17e
==================
Found 2 data race(s)
exit status 66
```
更多评论
改了下你的程序,在看到你这个问题之前我都没这么敲过命令都不知道`DATA RACE`,但是我花了几分钟研究了一下,下面是我改后的代码。只要确保变量不会同时出现在两个协程就行。上面虽然关闭了close的通道,但是程序执行很快的,所以两个变量可能存在竞争吧。
```go
// main.go
package main
import (
"fmt"
"log"
"sync"
"time"
)
type runner struct {
mutableCh chan func()
close chan struct{}
ok chan struct{}
}
var pool = &sync.Pool{
New: func() interface{} {
return new(runner)
},
}
func newRunner() *runner {
r := pool.Get().(*runner)
r.mutableCh = make(chan func())
r.close = make(chan struct{})
r.ok = make(chan struct{})
return r
}
func (r *runner) send(fn func()) error {
wait := make(chan struct{})
var err error
r.mutableCh <- func() {
defer func() {
if e := recover(); e != nil {
log.Printf("%s", e)
err = fmt.Errorf("error: %s", e)
close(wait)
}
}()
fn()
close(wait)
}
<-wait
return err
}
func (r *runner) start() {
go func() {
for {
select {
case fn := <-r.mutableCh:
fn()
case <-r.close:
r.ok <- struct{}{}
return
}
}
}()
}
func (r *runner) stop() {
close(r.close)
}
func releaserunner(s *runner) {
<-s.ok
s.mutableCh = nil
s.close = nil
pool.Put(s)
}
func main() {
r := newRunner()
defer releaserunner(r)
r.start()
// err1 := r.send(func() {
// fmt.Println("send---")
// })
// fmt.Println(err1)
r.stop()
time.Sleep(time.Second * 1)
}
```
#1