2013-08-08 4 views
6

Ich versuche zu verstehen, was passiert, wenn Sie gleichzeitig Zugriff auf eine Zeiger Methoden machen?Go gleichzeitiger Zugriff auf Zeiger Methoden

Ich habe eine Karte von Zeigern und spawn ein paar Go-Routinen. Ich gebe die Karte in jede Go-Routine und jede Go-Routine verwendet einen der Werte in der Map. Nichts wird auf die Karte geschrieben, von der nur gelesen wird.

Die Karte ist klein, nur 4 Tasten, daher ist es möglich, dass mehr als eine Routine den gleichen Wert von der Karte verwendet.

Frage ist, was passiert, wenn zwei Go-Routinen eine Methode des gleichen Zeigers aufrufen? Werde ich unvorhersehbare Ergebnisse bekommen?

EDIT

Beispiel: Ich nehme die Karte Teil aus wie, dass ich nach dem nicht die Frage ist bin.

Ich habe foo, die ein Zeiger des Typs MyStruct ist und diese Struktur hat eine Methode DoSomething, die Argumente dauert. In der main Funktion erstelle ich zwei go routines und beide von ihnen Anrufe zu foo.DoSomething, die verschiedene Werte führen. In diesem Beispiel hat die erste Go-Routine eine viel größere Berechnung als die zweite (hier werden nur die Sleep-Zeiten verwendet, um Berechnungen zu simulieren). Wiederum ändert sich nichts in der Struktur. Ich rufe nur die Strukturmethode an. Muss ich mir Sorgen machen, dass die zweite Routine einen Anruf an foo.DoSomething macht, wenn die erste Routine noch mit der Methode arbeitet?

package main 

import (
    "log" 
    "time" 
) 

type MyStruct struct { 
} 

func (self *MyStruct) DoSomething(value int) { 

    log.Printf("%d Start", value) 

    calculation_time := time.Duration(value) * time.Second 
    log.Printf("%d Calculating", value, calculation_time) 
    time.Sleep(calculation_time) 

    log.Printf("%d Done", value) 
} 

func main() { 

    var foo = new(MyStruct) 

    go foo.DoSomething(5) 

      // is this method call a problem when the first one is still working? 
    go foo.DoSomething(2) 

    time.Sleep(time.Duration(6 * time.Second)) 
} 

Antwort

7

Go-Methoden haben Empfänger. Receiver kann ein Zeigertyp sein.Ein Verfahren mit der Signatur, zum Beispiel:

func (r *R) foo(bar baz) // A method 

ist die same als

func foo(r *R, bar baz) // A plain old function 

Mit anderen Worten, der Empfänger, Zeigern oder nicht, ist nur ein Argument benutzt. Ihre Frage reduziert sich nun zu:

Was passiert, wenn zwei Routinen die obige Funktion mit dem gleichen Wert von r aufrufen?

A: Es kommt darauf an. Problemkonfigurationen:

  • foo ist aus irgendeinem Grund nicht einspringend.
  • foo mutiert *r (der Pointee) ohne Koordination/Synchronisation.
  • Der letzte Punkt ist nur ein Spezialfall: Wenn foo mutiert jeder Staat geteilt w/o Koordination/Synchronisation: etwas passieren kann.

foo Wenn die obigen teergruben vermeidet dann es ist sicher für durch mehrere goroutines sogar mit dem gleichen Wert von R gleichzeitig ausgeführt werden.

2

Jeder Zeiger gilt als nicht threadsicher. Eine Go-Routine sollte als separater Thread behandelt werden, auch wenn dies nicht der Fall ist. Go-Routinen werden über os-Threads gemultiplext.

Wenn der Wert immer schreibgeschützt ist (wird sich nie ändern), können Sie von beliebig vielen Routinen lesen. Sobald Sie den Wert ändern, erhalten Sie inkonsistente Ergebnisse.

Um den Zugriff zu synchronisieren und Probleme (und mögliche Paniken) zu vermeiden, müssen Sie eine sync.RWMutex verwenden. Anstatt also direkt zu lesen/schreiben, verwenden Sie eine Getter- und Setter-Funktion. Der Getter würde m.RLock() und verwenden. Der Setzer würde m.Lock() und m.Unlock() verwenden.

Wenn Sie Mutexe verwenden, versuchen Sie so schnell wie möglich zu entsperren. Halten Sie den Code zwischen einem sperren und entsperren so kurz wie möglich:

m.Lock() 
// Do what you need to do for the lock 
mymap[key] = value 
m.Unlock() 
// Do everything else here 

sync.RWMutex von sync.Mutex unterscheidet, dass es Ihnen so viele gleichzeitige Leser haben können, wie Sie möchten (RLOCK steht für Read-Lock). Sobald ein Autor versucht, eine Sperre zu ergreifen, verhindert er, dass andere Leser eine Sperre erhalten, und wartet darauf, dass die austretenden Leser ihre Sperren freigeben.

Alternativ können Sie Kanäle verwenden, um Werte zwischen Go-Routinen zu übergeben. Kanäle funktionieren in vielen Situationen und werden ermutigt. Sie können mehr über Nebenläufigkeit in Effective Go lesen. Die Kanäle passen jedoch nicht immer zu jeder Situation, es hängt also von der Situation ab.

+0

danke für Ihre Zeit bei der Hilfe, aber das ist nicht genau die Frage, die ich suchte. Ich habe eine Bearbeitung mit einem Codebeispiel hinzugefügt, von dem ich versuche, herauszufinden. Entschuldigung für jede Verwirrung. – Jeff

+0

@Jeff So wie meine Antwort sagt, "Wenn der Wert immer schreibgeschützt ist (wird sich nie ändern), können Sie von so vielen Routinen lesen, wie Sie wollen.". Es muss nicht kartenspezifisch sein - es ist ein Zeiger. Gleiches gilt. – Luke

1

Sie erhalten eine Race-Bedingung, wenn jemand eine Variable ändert, während jemand anders sie liest. In Ihrem Beispiel wird die Variable foo/self von mehreren goroutines gleichzeitig gelesen, aber da niemand sie während des gleichzeitigen Zugriffs ändert, ist alles in Ordnung.

Ein interessanteres Beispiel wäre eine Struktur MyStruct mit einigen zusätzlichen Attributen, z.B. attribute1. In diesem Fall gelten die gleichen Regeln auch für das Attribut. Sie können es von verschiedenen Goroutines gleichzeitig lesen - zum Beispiel könnte Ihre DoSomething Methode auch den Wert ausdrucken - aber Sie dürfen es während dieser Dauer nicht ändern.

Wenn Sie in der Lage sein möchten, eine Variable zu ändern, während auf sie zugegriffen wird, benötigen Sie eine Art von Synchronisationsgrundelement. Der idiomatische Go-Ansatz würde sein, gleichzeitige Zugriffe auf die gleiche Variable insgesamt zu vermeiden, indem nur lokale Variablen verwendet werden, auf die nur von einer einzigen Goroutine zugegriffen wird, und über Kanäle kommunizieren, wenn Sie Daten von einer anderen Goroutine benötigen.

Alternative Ansätze, die auch in Go verfügbar sind, sind die Grundelemente aus dem sync-Paket, wie sync.Mutex. Das -Paket bietet auch mehr fain-grained-Primitive, die zum Laden/Speichern/Modifizieren/Vergleichen und Tauschen von Variablen verwendet werden können.