2008-10-09 18 views
5

Tôi có một số vấn đề trên trang web có quyền truy cập đồng thời vào danh sách. Danh sách này giữ một giỏ hàng các mặt hàng và nhiều lần xóa sẽ làm hỏng trang web. Phương pháp nào tốt nhất để đồng bộ hóa chúng? Khóa có đủ không? Tùy chọn khóa có vẻ xấu xí vì mã được lan truyền khắp nơi và khá lộn xộn.Làm cách nào để đồng bộ hóa quyền truy cập vào Danh sách <T> được sử dụng trong ASP.NET?

Cập nhật: Đây là danh sách thực hiện như thế này: public class MyList: Danh sách < SomeCustomType> {}

Đây là một trang web di sản vì vậy không quá nhiều thay đổi được phép điều đó. Làm thế nào tôi nên cấu trúc lại điều này để khóa an toàn khi lặp qua nó?

bất kỳ ý tưởng nào!

Trả lời

7

Vì vậy, danh sách trong bộ nhớ này có được chia sẻ giữa các yêu cầu không? Điều đó nghe có vẻ giống như một nguyên nhân tiềm ẩn của vấn đề ngay cả khi bạn đã khóa. Các bộ sưu tập chung có thể chia sẻ nói chung nên tránh, IME.

Lưu ý rằng nếu bạn làm quyết định đồng bộ hóa, bạn sẽ cần phải làm những việc như khóa danh sách cho toàn bộ quá trình lặp qua nó và các hoạt động hợp chất khác. Chỉ cần khóa trên mỗi truy cập cụ thể là không đủ tốt.

4

Sử dụng khóa là cách chính xác để đồng bộ hóa quyền truy cập vào bộ sưu tập chung. truy cập vào bộ sưu tập được lan truyền khắp nơi, bạn có thể tạo một lớp bao bọc để bọc truy cập vào bộ sưu tập sao cho bề mặt tương tác bị giảm. Sau đó, bạn có thể giới thiệu đồng bộ hóa bên trong đối tượng gói.

4

Vâng, bạn phải sử dụng lock- List.SyncRoot như thế này:

lock (myList.SyncRoot) 
{ 
    // Access the collection. 
} 
+0

Thực ra điều đó không hiệu quả vì Danh sách <> triển khai ICollection.SyncRoot một cách rõ ràng. Để sử dụng, trước tiên bạn phải truyền danh sách của tôi đến ICollection ví dụ: khóa ((ICollection) myList) .SyncRoot) {...} – user430788

0

@Samuel Đó chính là điểm: Bạn KHÔNG thể sửa chữa một vấn đề như thế này chỉ bằng cách thay đổi các lớp hiện có. Tuy nhiên, việc giữ nguyên cách thức MS hầu như tránh các phương pháp ảo (chỉ khi MS dự định ghi đè chúng làm cho nó ảo, điều này có ý nghĩa nhưng có thể làm cho cuộc sống khó khăn hơn trong các tình huống mà bạn cần hack). Ngay cả khi nó nhúng List<T> và tất cả quyền truy cập vào nó đã đi qua mã bạn có thể thay đổi, bạn không thể đơn giản sửa đổi mã này thêm khóa để giải quyết vấn đề.

Tại sao? Vâng, vòng lặp cho một điều. Một bộ sưu tập không thể đơn thuần không được sửa đổi giữa mỗi lần truy cập vào bộ sưu tập. Nó không thể được sửa đổi trong toàn bộ liệt kê.

Trong thực tế, đồng bộ hóa là một vấn đề phức tạp và phụ thuộc vào danh sách thực sự là gì và những thứ gì cần phải đúng vào mọi lúc trong hệ thống.

Để minh họa, đây là một hành vi lạm dụng IDisposable. Điều này nhúng danh sách và redeclares bất kỳ chức năng của danh sách được sử dụng ở đâu đó, để truy cập vào danh sách có thể được đồng bộ hóa. Ngoài ra, nó không triển khai IEnumerable. Thay vào đó, cách duy nhất để truy cập vào liệt kê trong danh sách là thông qua một phương thức trả về một loại dùng một lần. Loại này đi vào màn hình khi được tạo và thoát ra khi được xử lý. Điều này đảm bảo danh sách không thể truy cập được trong khi lặp lại. Tuy nhiên, điều này vẫn chưa thực sự đủ, như sử dụng ví dụ của tôi sẽ minh họa.

đầu tiên trong danh sách bị tấn công:

public class MyCollection { object syncRoot = new object(); List list = new List();

public void Add(T item) { lock (syncRoot) list.Add(item); } 

public int Count 
{ 
    get { lock (syncRoot) return list.Count; } 
} 

public IteratorWrapper GetIteratorWrapper() 
{ 
    return new IteratorWrapper(this); 
} 


public class IteratorWrapper : IDisposable, IEnumerable<T> 
{ 
    bool disposed; 
    MyCollection<T> c; 
    public IteratorWrapper(MyCollection<T> c) { this.c = c; Monitor.Enter(c.syncRoot); } 
    public void Dispose() { if (!disposed) Monitor.Exit(c.syncRoot); disposed = true; } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return c.list.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

}

Sau đó, một ứng dụng giao diện điều khiển sử dụng nó:

class Program { static MyCollection strings = new MyCollection();

static void Main(string[] args) 
{ 
    new Thread(adder).Start(); 
    Thread.Sleep(15); 
    dump(); 
    Thread.Sleep(125); 
    dump(); 
    Console.WriteLine("Press any key."); 
    Console.ReadKey(true); 
} 

static void dump() 
{ 
    Console.WriteLine(string.Format("Count={0}", strings.Count).PadLeft(40, '-')); 
    using (var enumerable = strings.GetIteratorWrapper()) 
    { 
     foreach (var s in enumerable) 
      Console.WriteLine(s); 
    } 
    Console.WriteLine("".PadLeft(40, '-')); 
} 

static void adder() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     strings.Add(Guid.NewGuid().ToString("N")); 
     Thread.Sleep(7); 
    } 
} 

}

Lưu ý "đổ" phương pháp: Nó Đếm truy cập, đó là vô nghĩa khóa trong một nỗ lực để làm cho nó "thread an toàn", và sau đó lặp qua các mục. Nhưng có một điều kiện đua giữa Count getter (khóa, nhận số đếm, sau đó giải phóng) và câu lệnh sử dụng. Vì vậy, nó có thể không đổ số lượng các mặt hàng nó những điều nó làm.

Ở đây, điều đó có thể không quan trọng. Nhưng nếu mã thay vì làm một cái gì đó như:

var a = new string[strings.Count]; 
for (int i=0; i < strings.Count; i++) { ... } 

Hoặc thậm chí nhiều khả năng những thứ dơ lên:

var n = strings.Count; 
var a = new string[n]; 
for (int i=0; i < n; i++) { ... } 

Cựu sẽ nổ tung nếu mục được đồng thời bổ sung vào danh sách. Cái sau không bị hỏng nếu các mục bị xóa khỏi danh sách. Và trong cả hai trường hợp, ngữ nghĩa của mã có thể không bị ảnh hưởng bởi các thay đổi đối với danh sách, ngay cả khi các thay đổi không làm cho mã bị lỗi. Trong trường hợp đầu tiên, có lẽ các mục được xóa khỏi danh sách, khiến cho mảng không được lấp đầy. Sau đó một cái gì đó xa gỡ bỏ trong mã bị treo vì giá trị null trong mảng.

Bài học cần thực hiện là: Trạng thái được chia sẻ có thể rất phức tạp. Bạn cần một kế hoạch trả trước và bạn cần phải có chiến lược về cách bạn sẽ đảm bảo ý nghĩa là đúng.

Trong mỗi trường hợp này, hoạt động chính xác sẽ chỉ đạt được bằng cách đảm bảo khóa kéo dài tất cả các hoạt động liên quan. Có thể có nhiều câu lệnh khác ở giữa và khóa có thể mở rộng nhiều cuộc gọi phương thức và/hoặc liên quan đến nhiều đối tượng khác nhau. Không có viên đạn ma thuật nào cho phép khắc phục sự cố này chỉ bằng cách sửa đổi bộ sưu tập, vì việc đồng bộ hóa chính xác tùy thuộc vào bộ sưu tập nghĩa là. Một cái gì đó giống như một bộ nhớ đệm của các đối tượng chỉ đọc có thể được đồng bộ chỉ đối với add/remove/lookup, nhưng nếu bộ sưu tập chính nó là nghĩa vụ đại diện cho một số khái niệm có ý nghĩa/quan trọng, điều này sẽ không bao giờ là đủ.

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