2009-12-22 34 views
99

Tôi không thể tìm đủ thông tin trên các loại ConcurrentDictionary, vì vậy tôi nghĩ tôi sẽ hỏi về nó ở đây..NET - Khóa từ điển so với ConcurrentDictionary

Hiện tại, tôi sử dụng Dictionary để giữ tất cả người dùng được truy cập liên tục bởi nhiều luồng (từ một nhóm luồng, vì vậy không có số lượng chính xác luồng) và nó có quyền truy cập đồng bộ.

Gần đây tôi đã phát hiện ra rằng có một bộ sưu tập chuỗi an toàn trong .NET 4.0 và có vẻ như rất dễ chịu. Tôi đã tự hỏi, điều gì sẽ là tùy chọn 'hiệu quả hơn và dễ quản lý hơn', vì tôi có tùy chọn giữa việc có một số Dictionary bình thường với quyền truy cập được đồng bộ hóa hoặc có ConcurrentDictionary vốn đã an toàn.

Reference to .NET 4.0's ConcurrentDictionary

+2

Bạn có thể muốn xem [Bài dự án mã này về đề tài này] (http: //www.codeproject.com/Articles/548406/Dictionary-plus-Locking-versus-ConcurrentDictionar) – nawfal

Trả lời

124

Bộ sưu tập an toàn theo chủ đề so với bộ sưu tập không an toàn theo chủ đề có thể được xem xét theo một cách khác.

Hãy xem xét một cửa hàng không có nhân viên bán hàng, trừ khi thanh toán. Bạn có rất nhiều vấn đề nếu mọi người không hành động có trách nhiệm. Ví dụ, giả sử một khách hàng lấy một lon từ một kim tự tháp có thể trong khi một nhân viên bán hàng hiện đang xây dựng kim tự tháp, tất cả địa ngục sẽ vỡ ra. Hoặc, điều gì xảy ra nếu hai khách hàng đến cùng một mục cùng một lúc, ai sẽ thắng? Sẽ có một cuộc chiến? Đây là bộ sưu tập không an toàn. Có rất nhiều cách để tránh các vấn đề, nhưng tất cả chúng đều yêu cầu một số loại khóa hoặc truy cập rõ ràng theo cách này hay cách khác.

Mặt khác, hãy xem xét một cửa hàng với một nhân viên bán hàng tại bàn làm việc và bạn chỉ có thể mua sắm qua anh ta. Bạn nhận được trong dòng, và hỏi anh ta cho một mục, ông mang nó lại cho bạn, và bạn đi ra khỏi dòng. Nếu bạn cần nhiều mặt hàng, bạn chỉ có thể nhận nhiều mặt hàng trên mỗi chuyến khứ hồi như bạn có thể nhớ, nhưng bạn cần phải cẩn thận để tránh làm phiền nhân viên bán hàng, điều này sẽ khiến các khách hàng khác xếp hàng phía sau bạn.

Bây giờ hãy xem xét điều này. Trong cửa hàng với một nhân viên bán hàng, bạn sẽ làm gì nếu bạn đi đến phía trước của hàng và hỏi nhân viên bán hàng "Bạn có giấy vệ sinh nào không" và nói "Có", sau đó bạn "Ok, tôi" sẽ lấy lại cho bạn khi tôi biết bao nhiêu tôi cần ", sau đó bởi thời gian bạn trở lại ở phía trước của dòng, các cửa hàng có thể tất nhiên được bán ra. Kịch bản này không được ngăn chặn bởi một bộ sưu tập luồng.

Bộ sưu tập luồng an toàn đảm bảo rằng cấu trúc dữ liệu nội bộ của nó luôn hợp lệ, ngay cả khi được truy cập từ nhiều luồng.

Bộ sưu tập không an toàn không có bất kỳ bảo đảm nào như vậy. Ví dụ, nếu bạn thêm một cái gì đó vào cây nhị phân trên một sợi, trong khi một luồng khác đang bận tái cân bằng cây, không có gì đảm bảo mục sẽ được thêm vào, hoặc thậm chí cây đó vẫn còn hợp lệ sau đó, nó có thể bị hỏng ngoài hy vọng.

Một bộ sưu tập thread không, tuy nhiên, đảm bảo rằng các hoạt động tuần tự trên thread tất cả công việc trên cùng một "bản chụp" của cấu trúc dữ liệu nội bộ của mình, điều đó có nghĩa rằng nếu bạn có mã như thế này:

if (tree.Count > 0) 
    Debug.WriteLine(tree.First().ToString()); 

bạn có thể nhận được một NullReferenceException vì inbetween tree.Counttree.First(), một chủ đề khác đã xóa các nút còn lại trong cây, có nghĩa là First() sẽ trả lại null.

Đối với trường hợp này, bạn cần xem liệu bộ sưu tập đang đề cập có một cách an toàn để có được những gì bạn muốn, có lẽ bạn cần phải viết lại mã ở trên hoặc bạn có thể cần phải khóa.

+7

Mỗi khi tôi đọc bài viết này, mặc dù tất cả đều đúng, bằng cách nào đó chúng tôi đang đi sai đường dẫn. –

+18

Vấn đề chính trong ứng dụng hỗ trợ luồng là cấu trúc dữ liệu có thể thay đổi. Nếu bạn có thể tránh điều đó, bạn sẽ tiết kiệm được rất nhiều rắc rối. –

+0

Dường như từ điển có thể có một số hiệu ứng phụ thực sự khó chịu: http://stackoverflow.com/questions/14838032/asp-net-hang-generic-dictionary-concurrency-issues-causes-gc-deadlock – jocull

5

Về cơ bản bạn muốn đi với ConcurrentDictionary mới. Phải ra khỏi hộp, bạn phải viết ít mã hơn để tạo các chương trình an toàn cho luồng.

+0

có vấn đề gì nếu tôi tiếp tục với Dictionery Đồng thời không? – kbvishnu

53

Bạn vẫn cần phải rất cẩn thận khi sử dụng các bộ sưu tập theo chủ đề an toàn bởi vì an toàn chỉ không có nghĩa là bạn có thể bỏ qua tất cả các vấn đề về luồng. Khi một bộ sưu tập quảng cáo chính nó như là an toàn luồng, nó thường có nghĩa là nó vẫn ở trạng thái nhất quán ngay cả khi nhiều luồng đang đọc và viết đồng thời. Nhưng điều đó không có nghĩa là một chuỗi đơn sẽ thấy chuỗi kết quả "hợp lý" nếu nó gọi nhiều phương thức. Ví dụ: nếu trước tiên bạn kiểm tra xem khóa có tồn tại hay không và sau đó nhận được giá trị tương ứng với khóa, khóa đó có thể không còn tồn tại ngay cả với phiên bản ConcurrentDictionary nữa (vì một chuỗi khác có thể đã xóa khóa). Ví dụ: 0. Bạn vẫn cần phải sử dụng khóa trong trường hợp này (hoặc tốt hơn: kết hợp hai cuộc gọi bằng cách sử dụng TryGetValue).

Vì vậy, hãy sử dụng chúng, nhưng đừng nghĩ rằng nó cung cấp cho bạn một thẻ miễn phí để bỏ qua tất cả các vấn đề tương tranh. Bạn vẫn cần phải cẩn thận.

+4

Vì vậy, về cơ bản bạn đang nói nếu bạn không sử dụng các đối tượng chính xác nó sẽ không hoạt động? – ChaosPandion

+0

Tại sao vậy? Bộ sưu tập mất sự thay đổi, bằng cách nào đó? –

+1

Về cơ bản, bạn cần sử dụng TryAdd thay vì Contains chung -> Add. – ChaosPandion

12

Tôi nghĩ rằng phương thức ConcurrentDictionary.GetOrAdd chính xác là những gì hầu hết các trường hợp đa luồng cần.

+1

Tôi cũng sẽ sử dụng TryGet, nếu không bạn sẽ lãng phí công sức khi khởi tạo tham số thứ hai để GetOrAdd mỗi khi khóa đã tồn tại. –

+5

* GetOrAdd * có quá tải * GetOrAdd (TKey, Func ) *, vì vậy tham số thứ hai có thể được khởi tạo lười biếng chỉ trong trường hợp khóa không tồn tại trong từ điển. Bên cạnh đó * TryGetValue * được sử dụng để tìm nạp dữ liệu, không sửa đổi. Các cuộc gọi tiếp theo đến từng phương pháp này có thể khiến một luồng khác có thể đã thêm hoặc xóa khóa giữa hai cuộc gọi. – sgnsajgon

+0

Đây phải là câu trả lời được đánh dấu cho câu hỏi, mặc dù bài đăng của Lasse là tuyệt vời. – Spivonious

13

Bạn đã xem số Reactive Extensions cho .Net 3.5sp1 chưa. Theo Jon Skeet, họ đã quay trở lại một gói các phần mở rộng song song và cấu trúc dữ liệu đồng thời cho .Net3.5 sp1.

Có một bộ mẫu cho .Net 4 Beta 2, mô tả chi tiết khá tốt về cách sử dụng chúng các tiện ích mở rộng song song.

Tôi vừa trải qua tuần cuối cùng kiểm tra ConcurrentDictionary sử dụng 32 chủ đề để thực hiện I/O. Dường như nó hoạt động như quảng cáo, điều này cho thấy một lượng lớn thử nghiệm đã được đưa vào nó.

Chỉnh sửa: .NET 4 ConcurrentDictionary và mẫu.

Microsoft đã phát hành một bản pdf được gọi là Mẫu lập trình Paralell. Tải xuống giá trị thực của nó như được mô tả trong các chi tiết thực sự tốt đẹp các mẫu phù hợp để sử dụng cho .Net 4 Các phần mở rộng đồng thời và các mẫu chống để tránh. Here it is.

27

Nội bộ đồng thờiDictionary sử dụng khóa riêng cho mỗi nhóm băm. Miễn là bạn chỉ sử dụng Add/TryGetValue và các phương thức tương tự hoạt động trên các mục đơn lẻ, từ điển sẽ hoạt động như một cấu trúc dữ liệu gần như không khóa với lợi ích hiệu suất ngọt tương ứng. OTOH các phương pháp liệt kê (bao gồm cả thuộc tính Đếm) khóa tất cả các thùng cùng một lúc và do đó tồi tệ hơn một từ điển được đồng bộ hóa, hiệu quả-khôn ngoan.

Tôi muốn nói, chỉ cần sử dụng ConcurrentDictionary.

2

Chúng tôi đã sử dụng ConcurrentDictionary cho bộ sưu tập được lưu trong bộ nhớ cache, được điền lại sau mỗi 1 giờ và sau đó đọc theo nhiều chủ đề của khách hàng, tương tự to the solution cho câu hỏi Is this example thread safe?.

Chúng tôi nhận thấy rằng việc thay đổi thành ReadOnlyDictionary cải thiện hiệu suất tổng thể.

+2

Tôi đánh giá cao để biết lý do của downvote? –

+0

ReadOnlyDictionary chỉ là trình bao bọc xung quanh IDictionary khác. Những gì bạn sử dụng dưới mui xe? – Lu55

Các vấn đề liên quan