当多个goroutines对同一个数据进行操作时,可能会发生数据竞争问题(原子操作除外)。比如,在多goroutines下slice类型的append操作。多goroutines操作同一数据时,需要进行加锁处理.通过两个实例,来验证该问题.

不对数据进行保护:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    ips := make([]string, 0, 300)
    var wg sync.WaitGroup

    for i := 1; i < 254; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()

            t := strconv.Itoa(i)
            ip := "10.150.11." + t
            time.Sleep(100 * time.Millisecond)

            ips = append(ips, ip)
        }(i)
    }
    wg.Wait()

    fmt.Println(len(ips))
}

执行结果:

[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
247
[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
238
[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
243

        会发现,每次执行的结果ips slice数据的长度都不同。所以,执行的结果是错误的。在多个goroutine下,对数据append操作时,可能其它的goroutine任务也在进行append操作,这样的话就会出现数据竞争的问题。所以在进行对数据操作时,要将该数据保护起来,等操作完,再让其它的goroutine获取到该数据的执行权限.

对数据进行保护:

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    ips := make([]string, 0, 300)
    var (
    	wg sync.WaitGroup
    	m sync.Mutex
    )

    for i := 1; i < 254; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()

            t := strconv.Itoa(i)
            ip := "10.150.11." + t
            time.Sleep(100 * time.Millisecond)
            m.Lock()
            ips = append(ips, ip)
            m.Unlock()
        }(i)
    }
    wg.Wait()

    fmt.Println(len(ips))
}

执行结果:

[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
253
[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
253
[root@lg-pt-xnet-rsyslog04 demo]# go run main.go
253

将数据保护起来之后,执行结果就正确了.