2017-05-24 1 views
2

Ich habe bemerkt, dass, wenn ich auf einer Scheibe versucht anhängt goroutines innerhalb einer for Schleife, würde es Fälle geben, wo ich bekommen fehlt/leere Daten würden:Nicht thread-sicher anhängen?

destSlice := make([]myClass, 0) 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     destSlice = append(destSlice, tmpObj) 
    }(myObject) 
} 
wg.Wait() 

Manchmal, wenn ich drucken Sie alle AttributeName s von destSlice, einige Elemente sind leere Zeichenfolgen (""), und andere Male sind einige Elemente aus sourceSlice in destSlice nicht vorhanden.

Hat mein Code ein Datenrennen, und bedeutet dies, dass append nicht threadsicher für die gleichzeitige Verwendung durch mehrere Gloutines ist?

Antwort

4

In Go ist kein Wert für gleichzeitiges Lesen/Schreiben sicher, Slices (die slice headers sind) sind keine Ausnahme.

Ja, Ihr Code hat Datenrennen. Mit der Option -race zur Überprüfung ausführen.

Beispiel:

type myClass struct { 
    AttributeName string 
} 
sourceSlice := make([]myClass, 100) 

destSlice := make([]myClass, 0) 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     destSlice = append(destSlice, tmpObj) 
    }(myObject) 
} 
wg.Wait() 

es mit

go run -race play.go 

Ausgang Laufen ist:

================== 
WARNING: DATA RACE 
Read at 0x00c420074000 by goroutine 6: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x69 

Previous write at 0x00c420074000 by goroutine 5: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x106 

Goroutine 6 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 5 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
================== 
WARNING: DATA RACE 
Read at 0x00c42007e000 by goroutine 6: 
    runtime.growslice() 
     /usr/local/go/src/runtime/slice.go:82 +0x0 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0x1a7 

Previous write at 0x00c42007e000 by goroutine 5: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Goroutine 6 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 5 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
================== 
WARNING: DATA RACE 
Write at 0x00c420098120 by goroutine 80: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Previous write at 0x00c420098120 by goroutine 70: 
    main.main.func1() 
     /home/icza/gows/src/play/play.go:20 +0xc4 

Goroutine 80 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 

Goroutine 70 (running) created at: 
    main.main() 
     /home/icza/gows/src/play/play.go:21 +0x1cb 
================== 
Found 3 data race(s) 
exit status 66 

Lösung einfach ist, verwenden Sie einen sync.Mutex den destSlice Wert Schreiben zu schützen:

destSlice := make([]myClass, 0) 
mux := &sync.Mutex{} 

var wg sync.WaitGroup 
for _, myObject := range sourceSlice { 
    wg.Add(1) 
    go func(closureMyObject myClass) { 
     defer wg.Done() 
     var tmpObj myClass 
     tmpObj.AttributeName = closureMyObject.AttributeName 
     mux.Lock() 
     destSlice = append(destSlice, tmpObj) 
     mux.Unlock() 
    }(myObject) 
} 
wg.Wait() 

Sie könnten es auch auf andere Arten lösen, z. Du könntest einen Kanal benutzen, an den du den Wert senden würdest, der angehängt werden soll, und eine bestimmte Goroutine haben, die von diesem Kanal empfängt und den Append ausführt.