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