2009-11-01 30 views
7

Tôi có một vòng lặp Parallel.ForEach chạy một hoạt động chuyên sâu bên trong cơ thể.Sử dụng hashtable bên trong Parallel.ForEach?

Thao tác có thể sử dụng giá trị Hashtable để lưu trữ các giá trị và có thể được sử dụng lại cho các mục vòng lặp liên tiếp khác. Tôi thêm vào Hashtable sau khi hoạt động chuyên sâu hoàn tất, mục vòng lặp tiếp theo có thể tra cứu trong Hashtable và tái sử dụng đối tượng, thay vì chạy lại thao tác chuyên sâu.

Tuy nhiên, vì tôi đang sử dụng Parallel.ForEach có vấn đề không an toàn, khiến các cuộc gọi Hashtable.Add và ContainsKey (khóa) không đồng bộ, vì chúng có thể chạy song song. Giới thiệu ổ khóa có thể gây ra vấn đề về sự hoàn hảo.

Dưới đây là đoạn code mẫu:

Hashtable myTable = new Hashtable; 
Parallel.ForEach(items, (item, loopState) => 
{ 
    // If exists in myTable use it, else add to hashtable 
    if(myTable.ContainsKey(item.Key)) 
    { 
     myObj = myTable[item.Key]; 
    } 
    else 
    { 
     myObj = SomeIntensiveOperation(); 
     myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime 
    } 
    // Do something with myObj 
    // some code here 
} 

Phải có một số API, thiết lập tài sản bên trong thư viện TPL, mà có thể xử lý tình huống này. Lanhung?

Trả lời

18

Bạn đang tìm kiếm System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>. Các bộ sưu tập đồng thời mới sử dụng cơ chế khóa được cải thiện đáng kể và nên thực hiện tuyệt vời trong các thuật toán song song.

Edit: Kết quả có thể trông như thế này:

ConcurrentDictionary<T,K> cache = ...; 
Parallel.ForEach(items, (item, loopState) => 
{ 
    K value; 
    if (!cache.TryGetValue(item.Key, out value)) 
    { 
     value = SomeIntensiveOperation(); 
     cache.TryAdd(item.Key, value); 
    } 

    // Do something with value 
}); 

Lời cảnh báo: nếu các yếu tố trong items làm không phải tất cả có duy nhất item.Key, sau đó SomeIntensiveOperation có thể được gọi hai lần cho khóa đó. Trong ví dụ, khóa không được chuyển đến SomeIntensiveOperation, nhưng điều đó có nghĩa là mã "Làm điều gì đó có giá trị" có thể thực thi cặp khóa/giá trịA và cặp khóa/giá trịB và chỉ một kết quả sẽ được lưu trữ trong bộ nhớ cache (không nhất thiết là đầu tiên được tính bởi SomeIntensiveOperation). Bạn sẽ cần một nhà máy lười biếng song song để xử lý nếu đó là một vấn đề. Ngoài ra, vì lý do rõ ràng SomeIntensiveOperation nên được thread an toàn.

+1

@AdamRalph: kể từ khi ông đang sử dụng TPL thư viện ông đã sử dụng .net 4.0 –

+0

@Adam & Yassir: đúng, các bộ sưu tập mới được thiết kế với ý tưởng LINQ song song. –

+0

Yup Cảm ơn bạn đã trả lời và nhận xét – Vin

1

Tôi thấy không có sự lựa chọn chính xác nào khác ngoài việc sử dụng (nhiều hay ít rõ ràng) khóa (Một Hashtable được đồng bộ chỉ ghi đè tất cả các phương pháp có khóa).

Tùy chọn khác có thể là cho phép từ điển không đồng bộ hóa. Điều kiện chủng tộc sẽ không làm hỏng từ điển, nó sẽ chỉ yêu cầu mã để thực hiện một số tính toán thừa. Cấu hình mã để kiểm tra xem khóa hoặc thiếu bản ghi nhớ có ảnh hưởng xấu hơn không.

3

Sử dụng ReaderWriterLock, điều này có hiệu suất tốt cho công việc có nhiều lần đọc và ít lần viết có thời lượng ngắn. Vấn đề của bạn dường như phù hợp với đặc điểm kỹ thuật này.

Tất cả các thao tác đọc sẽ chạy nhanh và khóa miễn phí, thời gian duy nhất bất kỳ ai sẽ bị chặn là khi ghi đang diễn ra, và việc viết đó chỉ miễn là phải bỏ thứ gì đó trong Hashtable.

ReaderWriterLockSlim on MSDN

Tôi đoán tôi sẽ ném xuống một số mã ...

ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); 
Hashtable myTable = new Hashtable(); 
Parallel.ForEach(items, (item, loopState) => 
{ 
    cacheLock.EnterReadLock(); 
    MyObject myObj = myTable.TryGet(item.Key); 
    cacheLock.ExitReadLock(); 

    // If the object isn't cached, calculate it and cache it 
    if(myObj == null) 
    { 
     myObj = SomeIntensiveOperation(); 
     cacheLock.EnterWriteLock(); 
     try 
     { 
      myTable.Add(item.Key, myObj); 
     } 
     finally 
     { 
      cacheLock.ExitWriteLock(); 
     }   
    } 
    // Do something with myObj 
    // some code here 
} 

static object TryGet(this Hashtable table, object key) 
{ 
    if(table.Contains(key)) 
     return table[key] 
    else 
     return null; 
} 
+0

"Khuôn khổ .NET có hai ổ khóa đọc, ReaderWriterLockSlim và ReaderWriterLock. ReaderWriterLockSlim được khuyến khích cho tất cả phát triển mới. ReaderWriterLockSlim tương tự như ReaderWriterLock, nhưng nó đã đơn giản hóa quy tắc đệ quy và nâng cấp và ReaderWriterLockSlim tránh nhiều trường hợp bế tắc tiềm năng. Ngoài ra, hiệu suất của ReaderWriterLockSlim tốt hơn đáng kể so với ReaderWriterLock. " –

+0

Lời khuyên đó có vẻ hợp lý, vì vậy tôi đã cập nhật câu trả lời của mình. Đối với những người quan tâm, hãy xem bài viết trên tạp chí MSDN này: http://msdn2.microsoft.com/en-us/magazine/cc163599.aspx – joshperry

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