2012-10-17 31 views
6

Khi khác nhau, trên bản đồ m có nhà văn đồng thời, kể cả những người mà có thể xóa khỏi bản đồ, là nó không thread-safe để làm điều này ?:Có nhận được một giá trị sử dụng phạm vi không an toàn chỉ trong Go?

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

Tôi đang nghĩ là thread-safe tôi cần phải ngăn các nhà văn khác có thể thay đổi giá trị v trong khi tôi đang đọc nó và (khi sử dụng mutex và vì khóa là một bước riêng), hãy xác minh rằng khóa k vẫn còn trong bản đồ. Ví dụ:

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

(Giả sử rằng các tác giả khác được ghi khóa m trước khi thay đổi v.) Có cách nào tốt hơn?

Chỉnh sửa để thêm: Tôi biết rằng bản đồ không an toàn cho chủ đề. Tuy nhiên, chúng an toàn theo một cách, theo thông số Go tại http://golang.org/ref/spec#For_statements (tìm kiếm "Nếu các mục nhập bản đồ chưa đạt được bị xóa trong khi lặp lại"). Trang này chỉ ra rằng mã sử dụng range không cần phải lo lắng về các goroutines khác chèn vào hoặc xóa khỏi bản đồ. Câu hỏi của tôi là, sợi chỉ an toàn này có mở rộng đến v, sao cho tôi có thể nhận được vđể chỉ đọc chỉ sử dụng for k, v := range m và không có cơ chế an toàn chỉ khác? Tôi đã tạo một số mã thử nghiệm để cố gắng buộc ứng dụng bị lỗi để chứng minh rằng nó không hoạt động, nhưng thậm chí chạy mã blatantly thread-unsafe (rất nhiều goroutines tức giận sửa đổi cùng một giá trị bản đồ mà không có cơ chế khóa tại chỗ). get Go to crash!

+1

Chỉ cần một bình luận (ít hơn cho OP người có thể đã biết điều đó hơn cho độc giả khác): http://golang.org/doc/go_faq.html#atomic_maps –

Trả lời

9

Không, thao tác bản đồ không phải là nguyên tử/an toàn luồng, làm người nhận xét cho câu hỏi của bạn được chỉ đến the golang FAQ “Why are map operations not defined to be atomic?”.

Để bảo mật quyền truy cập của bạn, bạn được khuyến khích sử dụng gos channels làm phương tiện mã thông báo truy cập tài nguyên. Kênh được sử dụng để chỉ đơn giản là đi qua một mã thông báo. Bất cứ ai muốn sửa đổi nó sẽ yêu cầu như vậy từ kênh chặn hoặc không chặn. Khi làm xong với bản đồ, nó sẽ chuyển mã thông báo trở lại kênh.

Lặp lại và làm việc với bản đồ phải đủ đơn giản và ngắn gọn, vì vậy bạn chỉ nên sử dụng một mã thông báo để truy cập đầy đủ.

Nếu trường hợp đó không xảy ra và bạn sử dụng bản đồ cho những thứ phức tạp hơn/người tiêu dùng tài nguyên cần thêm thời gian với nó, bạn có thể thực hiện một trình đọc-truy cập-mã thông báo. Vì vậy, tại bất kỳ thời điểm nào, chỉ có một người viết có thể truy cập vào bản đồ, nhưng khi không có người viết nào hoạt động, mã thông báo được chuyển tới bất kỳ số lượng người đọc nào, người sẽ không sửa đổi bản đồ (vì vậy họ có thể đọc đồng thời).

Để biết giới thiệu về kênh, hãy xem the Effective Go docs on channels.

+0

Cảm ơn câu trả lời! Tôi đã chỉnh sửa câu hỏi của mình để nhấn mạnh rằng tôi tự hỏi nếu 'for k, v: = range m {...}' là an toàn chỉ cho 'v' chỉ đọc. Đặc tả Go chỉ ra rằng 'dải ô' là an toàn luồng theo như các goroutines khác chèn vào hoặc xóa khỏi bản đồ. Tôi biết về các kênh, nhưng nếu nhận được 'v' bằng cách sử dụng' phạm vi' đã được an toàn thread, đó là tất cả những gì tôi cần. – user1744397

+1

OK, nhìn xung quanh một số chi tiết khác, như [ở đây] (https://groups.google.com/forum /? fromgroups = #! searchin/golang-nuts/map $ 20range/golang-nuts/eJqwQLONhLs/3YFEjIe57wMJ), tôi thấy câu trả lời của bạn là chính xác. Dường như để an toàn chỉ cần khóa toàn bộ bản đồ trước khi sử dụng 'dải ô' trên đó, nếu các goroutines khác sẽ ghi vào (cập nhật, xóa hoặc sửa đổi) các khóa giống nhau được lặp lại. Phần từ đặc tả Go mà tôi đề cập đến đang nói về một chuỗi đơn lẻ đang xóa hoặc chèn vào bản đồ mà nó nằm trên, và chuỗi đó * ảnh hưởng như thế nào *. – user1744397

0

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