The sample a
func main(a) {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(a) {
fmt.Println(i) // Not the 'i' you are looking for.
wg.Done()
}()
}
wg.Wait()
Copy the code
Obviously, the I in the loop in the above code is read by multiple Goroutines at the same time, and the code execution result could be 44455 or 55555 instead of 01234
Ways to improve
func main(a) {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
fmt.Println(j) // Copy local variables
wg.Done()
}(i)
}
wg.Wait()
}
Copy the code
Example 2
(Share variables freely)
// Concurrent writing of file1 and file caused an error
func ParallelWrite(data []byte) chan error {
res := make(chan error, 2)
f1, err := os.Create("file1")
iferr ! =nil {
res <- err
} else {
go func(a) {
// This err is shared with the main goroutine,
// so the write races with the write below.
_, err = f1.Write(data)
res <- err
f1.Close()
}()
}
f2, err := os.Create("file2") // The second conflicting write to err.
iferr ! =nil {
res <- err
} else {
go func(a) {
_, err = f2.Write(data)
res <- err
f2.Close()
}()
}
return res
}
Copy the code
Goroutine: a variable in the external scope is shared within the goroutine, causing an error in reading the data
Improvement scheme (Note: use of =)
. _, err := f1.Write(data) ... _, err := f2.Write(data) ...Copy the code
Example 3
Unprotected global variable, concurrent reads of map
var service map[string]net.Addr
func RegisterService(name string, addr net.Addr) {
service[name] = addr
}
func LookupService(name string) net.Addr {
return service[name]
}
Copy the code
The method in the above code, if called using goroutine, will cause data contention
var (
service map[string]net.Addr
serviceMu sync.Mutex
)
func RegisterService(name string, addr net.Addr) {
serviceMu.Lock()
defer serviceMu.Unlock()
service[name] = addr
}
func LookupService(name string) net.Addr {
serviceMu.Lock()
defer serviceMu.Unlock()
return service[name]
}
Copy the code
Example 4 Protection of native data variables
type Watchdog struct{ last int64 } func (w *Watchdog) KeepAlive() { w.last = time.Now().UnixNano() // First conflicting access. } func (w *Watchdog) Start() { go func() { for { time.Sleep(time.Second) // Second conflicting access. if w.last < time.Now().Add(-10*time.Second).UnixNano() { fmt.Println("No keepalives for 10 seconds. Dying.") os.Exit(1) } } }() }Copy the code
Reading and modifying variables in the Watchdog structure described above will cause data competition. The solution is as follows (using the Sync /atomic package)
type Watchdog struct{ last int64 }
func (w *Watchdog) KeepAlive() {
atomic.StoreInt64(&w.last, time.Now().UnixNano())
}
func (w *Watchdog) Start() {
go func() {
for {
time.Sleep(time.Second)
if atomic.LoadInt64(&w.last) < time.Now().Add(-10*time.Second).UnixNano() {
fmt.Println("No keepalives for 10 seconds. Dying.")
os.Exit(1)
}
}
}()
}
Copy the code
The sample of five
c := make(chan struct{}) // or buffered channel // The race detector cannot derive the happens before relation // for the following send and close operations. These two operations // are unsynchronized and happen concurrently. go func() { c <- struct{}{} }() close(c)Copy the code
As above, the main program and goroutine are not synchronized, and the goroutine will be destroyed after the end of the main program. Improvement scheme:
c := make(chan struct{}) // or buffered channel
go func() { c <- struct{}{} }()
<-c
close(c)
Copy the code
When writing code, sometimes data races occur and are not easy to find, so we can add them at build time-race
Parameter to check whether there is a data race in our code, as shown in the following example
go test -race mypkg // to test the package
go run -race mysrc.go // to run the source file
go build -race mycmd // to build the command
go install -race mypkg // to install the package
Copy the code