2016-06-27 11 views
0

Ich habe über Gornotines und das Sync-Paket gelesen und meine Frage ist ... Muss ich immer entsperren, wenn das Schreiben von Daten auf verschiedenen goroutines lesen?Go Goroutine Sperre und entsperren

Zum Beispiel habe ich eine Variable auf meinem Server

config := make(map[string]string) 

Dann auf verschiedenen goroutines ich von Config lesen möchten. Ist es sicher, ohne Synchronisierung zu lesen, oder nicht?

Ich denke, das Schreiben muss mit dem Sync-Paket erfolgen. aber ich bin nicht sicher

Lesen

Zum Beispiel habe ich ein einfaches In-Memory-Cache-System

type Cache interface { 
    Get(key string) interface{} 
    Put(key string, expires int64, value interface{}) 
} 

// MemoryCache represents a memory type of cache 
type MemoryCache struct { 
    c map[string]*MemoryCacheValue 
    rw sync.RWMutex 
} 

// MemoryCacheValue represents a memory cache value 
type MemoryCacheValue struct { 
    value interface{} 
    expires int64 
} 

// NewMemoryCache creates a new memory cache 
func NewMemoryCache() Cache { 
    return &MemoryCache{ 
     c: make(map[string]*MemoryCacheValue), 
    } 
} 

// Get stores something into the cache 
func (m *MemoryCache) Get(key string) interface{} { 
    if v, ok := m.c[key]; ok { 
     return v 
    } 
    return nil 
} 

// Put retrieves something from the cache 
func (m *MemoryCache) Put(key string, expires int64, value interface{}) { 
    m.rw.Lock() 
    m.c[key] = &MemoryCacheValue{ 
     value, 
     time.Now().Unix() + expires, 
    } 
    m.rw.Unlock() 
} 

ich hier sicher bin handeln oder ich muß noch entsperren sperren, wenn ich will nur lesen?

Antwort

6

Sie tauchen ein in die Welt der Rennbedingungen. Die grundlegende Faustregel ist, dass, wenn ANY Routine schreibt oder ändert ein Teil der Daten, die durch eine beliebige Anzahl von anderen Korotinen/Threads gelesen werden können (oder auch geschrieben), müssen Sie eine Art von Synchronisation haben System an Ort und Stelle.

Zum Beispiel sagen wir, Sie haben diese Karte. Es hat ["Joe"] = "Smith" und ["Sean"] = "Howard" drin. Eine Goroutine möchte den Wert von ["Joe"] lesen. Eine andere Routine aktualisiert ["Joe"] zu "Cooper". Welchen Wert liest die erste goroutine? Abhängig davon, welche Goroutine zuerst zu den Daten gelangt. Das ist die Race Condition, das Verhalten ist unbestimmt und unvorhersehbar.

Die einfachste Methode zur Kontrolle dieses Zugriffs ist eine . In Ihrem Fall, da einige Routinen nur lesen und nicht schreiben müssen, können Sie stattdessen sync.RWMutex verwenden (der Hauptunterschied besteht darin, dass eine RWMutex eine beliebige Anzahl von Threads lesen kann, solange keiner versucht zu schreiben). Sie würden diese in die Karte backen eine Struktur wie folgt aus:

type MutexMap struct { 
    m map[string]string 
    *sync.RWMutex 
} 

Dann in Routinen, die von der Karte lesen müssen, würden Sie tun:

func ReadSomething(o MutexMap, key string) string { 
    o.RLock() // lock for reading, blocks until the Mutex is ready 
    defer o.RUnlock() // make SURE you do this, else it will be locked permanently 
    return o.m[key] 
} 

Und schreiben:

func WriteSomething(o MutexMap, key, value string) { 
    o.Lock() // lock for writing, blocks until the Mutex is ready 
    defer o.Unlock() // again, make SURE you do this, else it will be locked permanently 
    o.m[key] = value 
} 

Beachten Sie, dass beide als Methoden der Struktur geschrieben werden können, anstatt Funktionen, falls gewünscht.


Sie können dies auch über Kanäle erreichen. Sie erstellen eine Controllerstruktur, die in einer Goroutine ausgeführt wird, und Sie stellen über Kanäle Kanäle an. Beispiel:

package main 

import "fmt" 

type MapCtrl struct { 
    m  map[string]string 
    ReadCh chan chan map[string]string 
    WriteCh chan map[string]string 
    QuitCh chan struct{} 
} 

func NewMapController() *MapCtrl { 
    return &MapCtrl{ 
     m:  make(map[string]string), 
     ReadCh: make(chan chan map[string]string), 
     WriteCh: make(chan map[string]string), 
     QuitCh: make(chan struct{}), 
    } 
} 

func (ctrl *MapCtrl) Control() { 
    for { 
     select { 
     case r := <-ctrl.ReadCh: 
      fmt.Println("Read request received") 
      retmap := make(map[string]string) 
      for k, v := range ctrl.m { // copy map, so it doesn't change in place after return 
       retmap[k] = v 
      } 
      r <- retmap 
     case w := <-ctrl.WriteCh: 
      fmt.Println("Write request received with", w) 
      for k, v := range w { 
       ctrl.m[k] = v 
      } 
     case <-ctrl.QuitCh: 
      fmt.Println("Quit request received") 
      return 
     } 
    } 
} 

func main() { 
    ctrl := NewMapController() 
    defer close(ctrl.QuitCh) 
    go ctrl.Control() 

    m := make(map[string]string) 
    m["Joe"] = "Smith" 
    m["Sean"] = "Howard" 
    ctrl.WriteCh <- m 

    r := make(chan map[string]string, 1) 
    ctrl.ReadCh <- r 
    fmt.Println(<-r) 
} 

Runnable version

+1

Ich würde das gefälschten Rennen Beispiels loszuwerden, da es weder unterwegs Implementierung von Karten noch Strings gilt, und ist eine Art verwirrend. Sie haben auch "o" und "m" in den ersten Beispielen gemischt, zusammen mit dem Verweis auf die Mutex-Methoden als ein eingebettetes Feld, aber definieren es als "Mutex". – JimB

+0

Korrigiert nach Empfehlung. – Kaedys