2017-03-26 6 views
2

Ich habe eine Frage über Nebenläufigkeit in GoLang. Hier ist ein Beispielcode in golangNebenläufigkeit in Golang

package main 

import(
    "fmt" 
    "time" 
) 

var m int 
func add(i int){ 
    m++ 
} 

func main() { 
    m = 0 
    for i:=0;i<100;i++{ 
     go add(i) 
    } 
    time.Sleep(time.Millisecond * 1000) 
    fmt.Println(m) 
} 

Wenn ich es ausführen Ich habe immer das gleiche Ergebnis 100, auch wenn ich es mehrmals ausführen.

Wenn ich den gleichen Code in C (ohne Mutex), habe ich manchmal andere Ergebnisse.

Und meine Frage, würde ich gerne wissen, ob GoLang implizit den Zugriff auf eine gemeinsame Variable mit einem internen Mechanismus verwaltet?

Vielen Dank.

Antwort

4

Nr Zum Beispiel Ihr Programm,

$ go run -race dz00dz.go 
================== 
WARNING: DATA RACE 
Read at 0x000000595200 by goroutine 7: 
    main.add() 
    /home/peter/gopath/src/dz00dz.go:11 +0x3d 

Previous write at 0x000000595200 by goroutine 6: 
    main.add() 
     /home/peter/gopath/src/dz00dz.go:11 +0x59 

Goroutine 7 (running) created at: 
    main.main() 
     /home/peter/gopath/src/dz00dz.go:17 +0x76 

Goroutine 6 (finished) created at: 
    main.main() 
     /home/peter/gopath/src/dz00dz.go:17 +0x76 
================== 
100 
Found 1 data race(s) 
exit status 66 

Referenzen:

Introducing the Go Race Detector

+0

Danke, jetzt verstehe ich, warum :) – dz00dz

1

Sie haben eine Race-Bedingung hier, versuchen Sie, den Code zu testen mit:

go test -race 

Es bedeutet, dass m++ ist nicht Thread-sicher, versuchen, etwas wie folgt aus:

var (
    m int 
    mu *sync.RWMutex 
) 

func add(i int){ 
    mu.Lock() 
    m++ 
    mu.Unlock() 
} 

Auch ich sehe Dieser Code ist ein bisschen schmutzig: 1. Entfernen m = 0 2. Ersetzen Sleep durch WaitGroup 3. Warum passierst du i aber tun n Benutze es?

+0

Danke, jetzt verstehe ich, warum :) – dz00dz

+0

ich habe ich verwendet, bevor ich diesen Code zu veröffentlichen, und wenn ich diesen Code poste ich es vergessen Sie zu entfernen – dz00dz

2

ich die gleichen Ergebnisse hier bekommen, aber nicht, wenn ich ersetzen

m++ 

mit

func add(i int) { 
    for j := 0; j < 100000; j++ { 
     m++ 
    } 
} 

Im letzteren Fall bestätigte ich scheduler tracing mit, dass die Go-Laufzeit die Arbeit auf mehrere Prozessorkerne verteilt, was die Diskrepanz, denn in der Tat erklärt, ist m nicht gegen Rennbedingungen geschützt. Warum passiert das im ersten Fall nicht? Wahrscheinlich, weil ein einfaches Integer-Inkrement zu kurz ist, als dass der Go-Scheduler die Gcoutinen mehreren Threads zuordnen könnte, sodass sie nacheinander in demselben Thread ausgeführt werden. Und warum passiert es in C: Weil Sie die Berechnungen auf mehrere Threads manuell verteilen, kann der Betriebssystem-Scheduler entscheiden, sie auf mehreren Prozessorkernen auszuführen.

+0

Danke, jetzt verstehe ich warum :) – dz00dz

0

Nein, Sie können nicht das gleiche Ergebnis erhalten.

package main 

import (
    "fmt" 
    "time" 
) 

var m int 

func add(i int) { 
    m++ 
} 

func main() { 
    m = 0 
    for i := 0; i < 10000; i++ { 
     go add(i) 
    } 
    time.Sleep(time.Millisecond * 1000) 
    fmt.Println(m) 
} 
0
// This sample program demonstrates how to create goroutines and 
// how the goroutine scheduler behaves with three logical processors. 

package main 

import (
    "fmt" 
    "runtime" 
    "sync" 
) 

func main() { 
    // Allocate three logical processors for the scheduler to use. 
    runtime.GOMAXPROCS(3) 

    // processTest is used to wait for the program to finish. 
    var processTest sync.WaitGroup 
    // Add a count of three, one for each goroutine. 
    processTest.Add(3) 

    // Declaration of three anonymous function and create a goroutine. 
    go func() { 
     defer processTest.Done() 
     for i := 0; i < 30; i++ { 
      for j := 51; j <= 100; j++ { 
       fmt.Printf(" %d", j) 
       if j == 100{ 
        fmt.Println() 
       } 
      } 
     } 
    }() 
    go func() { 
     defer processTest.Done() 
     for j := 0; j < 10; j++ { 
      for char := 'A'; char < 'A'+26; char++ { 
       fmt.Printf("%c ", char) 
       if char == 'Z' { 
        fmt.Println() 
       } 

      } 
     } 
    }() 
    go func() { 
     defer processTest.Done() 
     for i := 0; i < 30; i++ { 
      for j := 0; j <= 50; j++ { 
       fmt.Printf(" %d", j) 
       if j == 50 { 
        fmt.Println() 
       } 
      } 
     } 
    }() 

    // Wait for the goroutines to finish. 
    processTest.Wait() 
} 

Wenn wir den Scheduler mehr als einen logischen Prozessor zu verwenden geben, werden wir ein anderes Verhalten in der Ausgabe unserer Beispielprogramme sehen. Wenn Sie das Programm ausführen, werden Sie sehen, dass die Goroutines parallel laufen. Mehrere Goroutinen beginnen zu laufen, und die Buchstaben und Zahlen im Display sind gemischt. Die Ausgabe basiert auf dem Ausführen des Programms auf einem Acht-Kern-Maschine, so dass jede Goroutine auf einem eigenen Kern ausgeführt wird. http://www.golangprograms.com/this-sample-program-demonstrates-how-to-create-multiple-goroutines-and-how-the-goroutine-scheduler-behaves-with-three-logical-processors.html