2012-10-17 2 views
6

Wenn über eine Karte hin m die gleichzeitige Schriftsteller, darunter diejenigen, die von der Karte gelöscht werden könnte, ist es nicht Thread-sicher dieseWird in Go ein Wert mit nicht threadsicherem Bereich angezeigt?

for k, v := range m { ... } 

Ich denke, Thread-sicher was ich tun ?: zu sein müssen andere mögliche Autoren zu verhindern, den Wert zu ändern v, während ich es bin zu lesen, und (bei Verwendung eines Mutex und weil Verriegelung einen separaten Schritt ist) überprüfen, ob der Schlüssel k noch in der Karte ist. Zum Beispiel:

for k := range m { 
    m.mutex.RLock() 
    v, found := m[k] 
    m.mutex.RUnlock() 
    if found { 
     ... // process v 
    } 
} 

(davon ausgehen, dass andere Autoren sind Schreibhemmung m vor v ändern.) Gibt es einen besseren Weg?

bearbeiten hinzuzufügen: Ich bin mir bewusst, dass die Karten nicht Thread-sicher sind. Sie sind jedoch nach der Go-Spezifikation unter http://golang.org/ref/spec#For_statements in einer Richtung threadsicher (Suche nach "Wenn Map-Einträge, die noch nicht erreicht wurden, während der Iteration gelöscht werden"). Diese Seite zeigt an, dass der Code, der range verwendet, sich nicht darum kümmern muss, dass andere Goroutines in die Karte eingefügt oder daraus gelöscht werden. Meine Frage ist, erweitert sich diese Gewinderesistenz auf v, so dass ich vzum Lesen nur mit nur for k, v := range m und keine anderen thread-sicheren Mechanismus bekommen kann? Ich habe einen Testcode erstellt, um zu versuchen, einen App-Absturz zu erzwingen, der beweist, dass er nicht funktioniert, aber sogar anstößigen thread-unsafe-Code laufen lässt (viele goroutines modifizieren den gleichen Map-Wert ohne Locking-Mechanismus) Geh zum Absturz!

+1

Nur ein Kommentar (weniger für OP, die wahrscheinlich weiß es bereits als für andere Leser): http://golang.org/doc/go_faq.html#atomic_maps –

Antwort

9

Nein, Kartenoperationen sind nicht atomar/threadsicher, da der Kommentar zu Ihrer Frage auf the golang FAQ “Why are map operations not defined to be atomic?” zeigt.

Um Ihren Zugriff auf sie zu sichern, werden Sie gos channels als Mittel der Ressourcenzugriffstoken verwenden ermutigt. Der Kanal wird verwendet, um einfach ein Token weiterzugeben. Jeder, der es ändern möchte, wird dies vom Kanal anfordern - blockierend oder nicht blockierend. Wenn Sie mit der Map arbeiten, wird das Token an den Kanal zurückgegeben.

iterieren und mit der Karte arbeiten, sollten ausreichend einfach und kurz sein, so dass Sie in Ordnung sein sollte nur ein Token für den Vollzugriff verwenden.

Wenn das nicht der Fall ist, und Sie die Karte für komplexere Sachen/a Ressourcenverbraucher mehr Zeit mit ihm brauchen, können Sie einen Leser- vs writer-access-Token umzusetzen. So kann zu einem bestimmten Zeitpunkt nur ein Schreiber auf die Karte zugreifen, aber wenn kein Schreiber aktiv ist, wird das Token an eine beliebige Anzahl von Lesern weitergegeben, die die Karte nicht modifizieren (somit können sie gleichzeitig lesen).

Eine Einführung in die Kanäle finden the Effective Go docs on channels.

+0

Danke für die Antwort! Ich habe meine Frage bearbeitet, um zu betonen, dass ich mich frage, ob 'for k, v: = range m {...}' threadsicher für 'v' ist, um nur zu lesen. Die Go-Spezifikation zeigt an, dass "Bereich" threadsicher ist, solange andere Goroutines in die Karte eingefügt oder daraus gelöscht werden. Ich kenne Kanäle, aber wenn ich 'v' mit' range' verwende, ist es bereits Thread-sicher, das ist alles was ich brauche. – user1744397

+1

OK, schauen Sie sich etwas mehr an, wie [hier] (https://groups.google.com/forum /? fromgroups = #! suche/golang-mutes/karte $ 20range/golang-nuts/eJqwQLONhLs/3YFEjIe57wMJ), ich sehe, dass deine Antwort richtig ist. Es scheint, dass es threadsicher sein muss, eine ganze Map zu sperren, bevor "range" verwendet wird, wenn andere goroutines dieselben Schlüssel schreiben (updaten, löschen oder modifizieren), die iteriert werden. Der Abschnitt aus der Go-Spezifikation, von dem ich gesprochen habe, spricht über einen einzelnen Thread, der aus der Map gelöscht wird oder in die Map eingefügt wird, über die sie sich erstreckt, und wie sich dieser Thread * auf ihn selbst auswirkt *. – user1744397

0

könnten Sie concurrent-map verwenden, um die Gleichzeitigkeit Schmerzen für Sie zu behandeln.

// Create a new map. 
map := cmap.NewConcurretMap() 

// Add item to map, adds "bar" under key "foo" 
map.Add("foo", "bar") 

// Retrieve item from map. 
tmp, ok := map.Get("foo") 

// Checks if item exists 
if ok == true { 
    // Map stores items as interface{}, hence we'll have to cast. 
    bar := tmp.(string) 
} 

// Removes item under key "foo" 
map.Remove("foo") 
Verwandte Themen