2009-07-02 42 views
8

Tôi có một tình huống trong C# nơi tôi có một danh sách các loại đơn giản. Danh sách này có thể được truy cập bởi nhiều chủ đề: các mục có thể được thêm vào hoặc loại bỏ, và sự tồn tại của một mục có thể được kiểm tra. Tôi đã đóng gói danh sách trong một đối tượng phơi bày chỉ ba hoạt động này cho đến nay.C# Danh sách câu hỏi đồng thời

Tôi có một vài trường hợp để xử lý (không chính xác giống như các phương pháp tôi vừa đề cập).
1. Chủ đề chỉ có thể kiểm tra sự tồn tại của mục nhập. (đơn giản)
2. Chủ đề có thể kiểm tra sự tồn tại của một mục nhập và nếu nó không tồn tại, hãy thêm nó.
3. Chủ đề cần kiểm tra xem có tồn tại một mục hay không và nếu có, hãy chờ cho đến khi nó được gỡ bỏ.
4. Một sự kết hợp của 2 và 3, trong đó một chủ đề kiểm tra sự tồn tại của một mục, nếu nó tồn tại, nó phải đợi cho đến khi nó được gỡ bỏ trước khi nó có thể tự thêm nó.

Toàn bộ ý tưởng là sự tồn tại của một mục có nghĩa là một khóa. Nếu một mục nhập tồn tại, đối tượng mà nó xác định không thể thay đổi và mã không thể tiến hành vì nó đang được sửa đổi ở nơi khác.

Đây có thể là những tình huống mới làm quen đơn giản nhưng tôi làm mới bản thân về các vấn đề tương tranh và nó khiến tôi hơi hoang tưởng, và tôi cũng không quen thuộc với các cơ chế đồng thời của C#.

Cách tốt nhất để xử lý việc này là gì? Tôi hoàn toàn tắt? Nên kiểm tra và thêm (thử nghiệm và thiết lập?) Được kết hợp thành một hoạt động nguyên tử thứ tư? Tôi chỉ đơn giản là sẽ thêm khối khóa vào phương pháp của tôi, nơi danh sách được truy cập?

Ngoài ra, có thể kiểm tra đơn vị loại điều này (không phải là thao tác đơn giản, tình huống đồng thời) không?

Trả lời

8

Thử nghiệm đơn vị chắc chắn sẽ khó.

Tất cả điều này có thể được thực hiện một cách hợp lý chỉ đơn giản bằng cơ chế đồng thời "gốc" trong .NET: báo cáo khóa và Monitor.Wait/Monitor.PulseAll. Trừ khi bạn có một màn hình riêng biệt cho mỗi mục mặc dù, bạn sẽ cần phải đánh thức tất cả các chủ đề bất cứ khi nào bất cứ điều gì được gỡ bỏ - nếu không bạn sẽ không thể nói cho chủ đề "đúng" để thức dậy.

Nếu nó thực sự chỉ là một thiết các hạng mục, bạn có thể muốn sử dụng HashSet<T> thay vì List<T> để đại diện cho bộ sưu tập, bằng cách này - không có gì bạn đã đề cập là để làm với đặt hàng.

Mẫu mã, giả định rằng một bộ không quan trọng cho bạn:

using System; 
using System.Collections.Generic; 
using System.Threading; 

public class LockCollection<T> 
{ 
    private readonly HashSet<T> items = new HashSet<T>(); 
    private readonly object padlock = new object(); 

    public bool Contains(T item) 
    { 
     lock (padlock) 
     { 
      return items.Contains(item); 
     } 
    } 

    public bool Add(T item) 
    { 
     lock (padlock) 
     { 
      // HashSet<T>.Add does what you want already :) 
      // Note that it will return true if the item 
      // *was* added (i.e. !Contains(item)) 
      return items.Add(item); 
     } 
    } 

    public void WaitForNonExistence(T item) 
    { 
     lock (padlock) 
     { 
      while (items.Contains(item)) 
      { 
       Monitor.Wait(padlock); 
      } 
     } 
    } 

    public void WaitForAndAdd(T item) 
    { 
     lock (padlock) 
     { 
      WaitForNonExistence(item); 
      items.Add(item); 
     } 
    } 

    public void Remove(T item) 
    { 
     lock (padlock) 
     { 
      if (items.Remove(item)) 
      { 
       Monitor.PulseAll(padlock); 
      } 
     } 
    } 
} 

(. Hoàn toàn chưa được kiểm tra, phải thừa nhận là Bạn có thể cũng muốn xác định timeout cho mã chờ đợi ...)

+0

+1, tôi đi đến một giải pháp tương tự nhưng HashSet làm cho nó hiệu quả hơn. Bản demo đẹp cho lớp Monitor, sử dụng khóa lồng nhau vv. Chỉ có vấn đề có thể là một 'stampede' khi một mục bị xóa. –

8

Trong khi # 1 có thể là cách đơn giản nhất để viết, nó chủ yếu là một phương pháp vô dụng. Trừ khi bạn đang nắm giữ trên cùng một khóa sau khi kết thúc một truy vấn cho "sự tồn tại của một mục", bạn đang thực sự trở về "sự tồn tại của một mục tại một số điểm trong quá khứ". Nó không cung cấp cho bạn bất kỳ thông tin nào về sự tồn tại của mục nhập hiện tại.

Giữa việc khám phá giá trị trong danh sách, sau đó thực hiện bất kỳ thao tác nào để truy xuất, xóa giá trị, một chuỗi khác có thể đến và xóa nó cho bạn.

Chứa các hoạt động trên danh sách đồng thời phải được kết hợp với thao tác bạn dự định thực hiện trong trường hợp tồn tại đúng/sai của séc đó.Ví dụ: TestAdd() hoặc TestRemove() là nhiều hơn an toàn hơn Chứa + Thêm hoặc Chứa + Xóa

+0

Bạn nên liên kết tới bài đăng trên blog của mình trên các giao diện thu thập an toàn của chủ đề. –

+0

Có, nhưng # 4 thực hiện điều đó. Khả năng sử dụng của các phương pháp khác là nghi ngờ, nhưng không nhất thiết là sai. –

+0

@Henk, tôi nghĩ rằng # 2- # 4 là phương pháp tốt. # 1 mặc dù không phải là sai nhưng nó chỉ không hữu ích. – JaredPar

1

Có một sản phẩm để tìm kiếm điều kiện chủng tộc và tương tự trong các thử nghiệm đơn vị. Nó được gọi là TypeMock Racer. Tôi không thể nói bất cứ điều gì cho hoặc chống lại hiệu quả của nó, mặc dù. :)