2016-06-27 1 views
0

Я читал о goroutines и пакете синхронизации, и мой вопрос ... Всегда ли мне нужно блокировать разблокировку при чтении писем к данным на разных goroutines?Go goroutine lock and unlock

Например, у меня есть переменная на моем сервере

config := make(map[string]string) 

Тогда на разных goroutines я хочу прочитать от конфигурации. Безопасно ли читать без использования синхронизации, или нет?

Я предполагаю, что написание должно быть выполнено с использованием пакета синхронизации. но я не уверен, что чтение

Например, у меня есть простая система кэширования в оперативной памяти

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() 
} 

Я действую здесь в безопасности или мне еще нужно заблокировать разблокировать, когда я хочу только читать?

ответ

6

Вы погружаетесь в мир условий гонки. Основное правило состоит в том, что если ANY выполняет запись или изменение части данных, которая может быть или считывается (или также записывается) любым количеством других сопрограмм/потоков, вам необходимо иметь какую-то синхронизацию система на месте.

Например, скажем, у вас есть эта карта. В нем есть [«Джо»] = «Смит» и [«Шон»] = «Говард». Один горутин хочет прочитать значение [«Джо»]. Другая рутина обновляет [«Джо»] до «Купера». Какое значение читает первый горутин? Зависит от того, какой канал сначала добирается до данных. Это состояние гонки, поведение не определено и непредсказуемо.

Самый простой способ контроля доступа - sync.Mutex. В вашем случае, поскольку некоторые процедуры требуют только чтения и записи, вы можете вместо этого использовать sync.RWMutex (основное отличие состоит в том, что RWMutex позволяет читать любое количество потоков, если никто не пытается их записать). Вы бы испечь это на карту, используя структуру, как это:

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

Затем, в процедуры, которые нужно читать с карты, вы могли бы сделать:

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] 
} 

И написать:

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 
} 

Обратите внимание, что оба они могут быть записаны как методы структуры, а не функции, если это необходимо.


Вы также можете подключиться к этому, используя каналы. Вы создаете структуру контроллера, которая работает в goroutine, и вы делаете запросы к ней по каналам. Пример:

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

Я бы избавиться от фальшивых, например гонок, так как ни один не относится к реализации идут карты, ни строк, и это своего рода заблуждение. В первых примерах вы также смешиваете 'o' и' m', а также ссылаетесь на методы Mutex как встроенное поле, но определяете его как 'mutex'. – JimB

+0

Исправлено по рекомендации. – Kaedys