没有互斥锁,并发处理切片无法按预期工作

Functions WithMutex and WithoutMutex are giving different results.

WithoutMutex implementation is losing values even though I have Waitgroup set up.

What could be wrong?

Do not run on Playground

P.S. I am on Windows 10 and Go 1.8.1

package main

import (
    "fmt"
    "sync"
)

var p = fmt.Println

type MuType struct {
    list []int
    *sync.RWMutex
}

var muData *MuType
var data *NonMuType

type NonMuType struct {
    list []int
}

func (data *MuType) add(i int, wg *sync.WaitGroup) {
    data.Lock()
    defer data.Unlock()
    data.list = append(data.list, i)
    wg.Done()

}

func (data *MuType) read() []int {
    data.RLock()
    defer data.RUnlock()
    return data.list
}

func (nonmu *NonMuType) add(i int, wg *sync.WaitGroup) {
    nonmu.list = append(nonmu.list, i)
    wg.Done()

}

func (nonmu *NonMuType) read() []int {
    return nonmu.list
}

func WithoutMutex() {
    nonmu := &NonMuType{}
    nonmu.list = make([]int, 0)
    var wg = sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go nonmu.add(i, &wg)

    }
    wg.Wait()
    data = nonmu
    p(data.read())
}

func WithMutex() {
    mtx := &sync.RWMutex{}
    withMu := &MuType{list: make([]int, 0)}
    withMu.RWMutex = mtx
    var wg = sync.WaitGroup{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go withMu.add(i, &wg)
    }
    wg.Wait()
    muData = withMu
    p(muData.read())
}

func stressTestWOMU(max int) {
    p("Without Mutex")
    for ii := 0; ii < max; ii++ {
        WithoutMutex()
    }
}

func stressTest(max int) {
    p("With Mutex")
    for ii := 0; ii < max; ii++ {
        WithMutex()
    }
}

func main() {
    stressTestWOMU(20)
    stressTest(20)
}

Slices are not safe for concurrent writes, so I am in no way surprised that WithoutMutex does not appear to be consistent at all, and has dropped items.

The WithMutex version consistently has 10 items, but in jumbled orders. This is also to be expected, since the mutex protects it so that only one can append at a time. There is no guarantee as to which goroutine will run in which order though, so it is a race to see which of the rapidly spawned goroutines will get to append first.

The waitgroup does not do anything to control access or enforce ordering. It merely provides a signal at the end that everything is done.