2012-06-16 6 views
55

Когда вы используете карту в программе с параллельным доступом, существует ли необходимость использовать мьютекс в функциях для , чтобы читать значения?Карта с одновременным доступом

+3

Если это строго только для чтения карты, мьютекс не требуется. – jimt

+0

Я был не очень ясен, так как будут функции для установки и получения значений. –

ответ

78

Несколько читателей, не писатели не в порядке:

https://groups.google.com/d/msg/golang-nuts/HpLWnGTp-n8/hyUYmnWJqiQJ

Один писатель, нет читателей не в порядке. (Карты не будут прока иначе.)

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

20

Я ответил на ваш вопрос в this Reddit нити несколько дней назад:

В Go, карты не потокобезопасные. Кроме того, данные требуют блокировки даже для чтения , если, например, может быть другой горутин, который является , записывая одни и те же данные (одновременно).

Судя по пояснениям в комментариях, что есть функции setter, ответ на ваш вопрос да, вы должны будете защитить свои чтения с помощью мьютекса; вы можете использовать RWMutex. В качестве примера вы можете посмотреть на source реализации структуры данных таблицы (использует карту за кулисами), которую я написал (фактически тот, что связан в потоке reddit).

+1

Это обычно расточительно использовать полную блокировку чтения-записи для ресурса, так быстро доступ как карту. – rmmh

+2

Можете ли вы немного рассказать? Что было бы лучше подходит? –

+9

Блокировки RW хороши для ресурсов с большим количеством споров, но у них больше накладных расходов, чем мьютексы. Map get/sets достаточно быстр, чтобы программа, вероятно, не имела достаточного количества аргументов, чтобы сделать более сложную блокировку более эффективной, чем простой мьютекс. – rmmh

16

Вы можете использовать concurrent-map, чтобы справиться с болями параллелизма.

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

// 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") 
+15

. Вот почему я не могу серьезно относиться к понятию «не нужны дженерики ». – Lucretiel

+9

Понятие не в том, что Go «не нужны дженерики», но «в настоящее время нет чистого способа реализации дженериков, нам нужно подумать об этом еще немного». C++, например, генерирует код для всех возможных сочетаний типов, которые используются, что увеличивает время компиляции и размеры исполняемого файла необоснованной суммой. – Alexander

1

Если у вас только один писатель, то вы, вероятно, можете уйти от использования атомного значения. Заимствован из https://golang.org/pkg/sync/atomic/#example_Value_readMostly следующим образом (оригинал использует блокировки для защиты письма, поэтому поддерживает несколько авторов)

type Map map[string]string 
    var m Value 
    m.Store(make(Map)) 

read := func(key string) (val string) { // read from multiple go routines 
      m1 := m.Load().(Map) 
      return m1[key] 
    } 

insert := func(key, val string) { // update from one go routine 
      m1 := m.Load().(Map) // load current value of the data structure 
      m2 := make(Map)  // create a new map 
      for k, v := range m1 { 
        m2[k] = v // copy all data from the current object to the new one 
      } 
      m2[key] = val // do the update that we need (can delete/add/change) 
      m.Store(m2) // atomically replace the current object with the new one 
      // At this point all new readers start working with the new version. 
      // The old version will be garbage collected once the existing readers 
      // (if any) are done with it. 
    } 
37

sync.Map слился Пойти мастер по состоянию на 27 апреля, 2017.

Это параллельная карта мы все ждали.

https://github.com/golang/go/blob/master/src/sync/map.go

https://godoc.org/sync#Map

+1

Ницца. Обратите внимание, что новый тип sync.Map предназначен для карт только для приложений (по этой причине он не использует очертание ключей, а это означает, что если ваша карта имеет много оттока, она, скорее всего, будет отставать от карт с осколками в соответствии с мой ответ выше). – orcaman

+0

@orcaman, что вы имеете в виду по картам только для приложения? – OmarIlias

+0

@OmarIlias Я имею в виду случаи, когда вы в основном устанавливаете новые значения (без редактирования или удаления существующих значений). См. Это: https://github.com/golang/go/issues/20360 – orcaman

0

Почему не использовал модель Go параллелизм вместо этого, не существует простой пример ...

type DataManager struct { 
    /** This contain connection to know dataStore **/ 
    m_dataStores map[string]DataStore 

    /** That channel is use to access the dataStores map **/ 
    m_dataStoreChan chan map[string]interface{} 
} 

func newDataManager() *DataManager { 
    dataManager := new(DataManager) 
    dataManager.m_dataStores = make(map[string]DataStore) 
    dataManager.m_dataStoreChan = make(chan map[string]interface{}, 0) 
    // Concurrency... 
    go func() { 
     for { 
      select { 
      case op := <-dataManager.m_dataStoreChan: 
       if op["op"] == "getDataStore" { 
        storeId := op["storeId"].(string) 
        op["store"].(chan DataStore) <- dataManager.m_dataStores[storeId] 
       } else if op["op"] == "getDataStores" { 
        stores := make([]DataStore, 0) 
        for _, store := range dataManager.m_dataStores { 
         stores = append(stores, store) 
        } 
        op["stores"].(chan []DataStore) <- stores 
       } else if op["op"] == "setDataStore" { 
        store := op["store"].(DataStore) 
        dataManager.m_dataStores[store.GetId()] = store 
       } else if op["op"] == "removeDataStore" { 
        storeId := op["storeId"].(string) 
        delete(dataManager.m_dataStores, storeId) 
       } 
      } 
     } 
    }() 

    return dataManager 
} 

/** 
* Access Map functions... 
*/ 
func (this *DataManager) getDataStore(id string) DataStore { 
    arguments := make(map[string]interface{}) 
    arguments["op"] = "getDataStore" 
    arguments["storeId"] = id 
    result := make(chan DataStore) 
    arguments["store"] = result 
    this.m_dataStoreChan <- arguments 
    return <-result 
} 

func (this *DataManager) getDataStores() []DataStore { 
    arguments := make(map[string]interface{}) 
    arguments["op"] = "getDataStores" 
    result := make(chan []DataStore) 
    arguments["stores"] = result 
    this.m_dataStoreChan <- arguments 
    return <-result 
} 

func (this *DataManager) setDataStore(store DataStore) { 
    arguments := make(map[string]interface{}) 
    arguments["op"] = "setDataStore" 
    arguments["store"] = store 
    this.m_dataStoreChan <- arguments 
} 

func (this *DataManager) removeDataStore(id string) { 
    arguments := make(map[string]interface{}) 
    arguments["storeId"] = id 
    arguments["op"] = "removeDataStore" 
    this.m_dataStoreChan <- arguments 
} 
Смежные вопросы