2012-06-16 26 views
55

Khi bạn sử dụng bản đồ trong một chương trình có quyền truy cập đồng thời, có cần sử dụng mutex trong hàm để đọc giá trị không?Bản đồ có quyền truy cập đồng thời

+3

Nếu nó là đúng một chỉ đọc bản đồ, một mutex không cần thiết. – jimt

+0

Tôi không rõ lắm vì sẽ có các chức năng để đặt và nhận các giá trị. –

Trả lời

78

Nhiều độc giả, không có tác giả là okay:

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

Một nhà văn, không có độc giả là okay. (Maps sẽ không được nhiều tốt khác.)

Nếu không, nếu có ít nhất một nhà văn và ít nhất một hoặc nhiều nhà văn hoặc người đọc, sau đó tất cả độc giả nhà văn phải sử dụng đồng bộ để truy cập bản đồ . Một mutex hoạt động tốt cho việc này.

20

Tôi đã trả lời câu hỏi của bạn trong this reddit chủ đề cách đây vài ngày:

Trong Go, bản đồ không được thread-safe. Ngoài ra, dữ liệu yêu cầu khóa ngay cả đối với ví dụ đọc nếu có thể có một goroutine khác là ghi cùng một dữ liệu (đồng thời, tức là).

Đánh giá bằng cách làm rõ trong các nhận xét, rằng cũng sẽ có chức năng setter, câu trả lời cho câu hỏi của bạn là có, bạn sẽ phải bảo vệ đọc bằng mutex; bạn có thể sử dụng RWMutex. Ví dụ, bạn có thể xem source về việc thực hiện cấu trúc dữ liệu bảng (sử dụng bản đồ đằng sau hậu trường) mà tôi đã viết (thực ra là một liên kết trong chuỗi reddit).

+1

thường lãng phí khi sử dụng khóa người đọc đầy đủ dành cho người đọc để truy cập tài nguyên nhanh như bản đồ – rmmh

+2

Bạn có thể giải thích một chút không? Điều gì sẽ phù hợp hơn? –

+9

Ổ khóa RW tốt cho các tài nguyên có nhiều tranh chấp, nhưng chúng có nhiều chi phí hơn một mutex. Bản đồ nhận/bộ là đủ nhanh mà chương trình có khả năng sẽ không có đủ tranh chấp để làm cho khóa phức tạp hơn cho thông lượng tốt hơn so với một mutex đơn giản. – rmmh

16

Bạn có thể sử dụng concurrent-map để xử lý các cơn đau đồng thời cho bạn.

// 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

Những điều như thế này là lý do tại sao tôi không thể coi trọng khái niệm " cần generics. " – Lucretiel

+9

Khái niệm không phải là Go "không cần generics" nhưng "hiện không có cách nào sạch để thực hiện generics, chúng ta cần phải suy nghĩ về điều này một số chi tiết". Ví dụ C++ tạo mã cho tất cả các kiểu kết hợp có thể được sử dụng, làm tăng thời gian biên dịch và kích cỡ thực thi bởi một số lượng không hợp lý. – Alexander

1

nếu bạn chỉ có một người viết, thì có thể bạn có thể lấy đi bằng cách sử dụng Giá trị nguyên tử. Sau đây là chuyển thể từ https://golang.org/pkg/sync/atomic/#example_Value_readMostly (bản gốc sử dụng ổ khóa để bảo vệ văn bản, để hỗ trợ nhiều tác giả)

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 đã sáp nhập để Tới chủ tính đến tháng 27, năm 2017.

Đây là Bản đồ đồng thời chúng tôi tất cả đã chờ đợi.

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

https://godoc.org/sync#Map

+1

Đẹp nhất. Lưu ý rằng loại sync.Map mới được thiết kế cho các bản đồ chỉ chắp thêm (vì lý do này không sử dụng các khóa, có nghĩa là nếu bản đồ của bạn có nhiều khả năng bị hỏng thì rất có thể sẽ làm tốt hơn bản đồ kiểu phân đoạn theo câu trả lời của tôi ở trên). – orcaman

+0

@orcaman, bạn có ý nghĩa gì với các bản đồ chỉ nối thêm? – OmarIlias

+0

@OmarIlias Tôi có nghĩa là các trường hợp bạn chủ yếu đặt giá trị mới (không cần chỉnh sửa hoặc xóa các giá trị hiện có). Xem này: https://github.com/golang/go/issues/20360 – orcaman

0

Tại sao không đã sử dụng mô hình đồng thời Go thay vào đó, có một ví dụ đơn giản ...

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 
} 
Các vấn đề liên quan