2011-12-21 26 views
5

Tôi đang xem xét một mã ví dụ trong một cuốn sách và đi qua đoạn mã sau (đơn giản hóa). Trong mã, khi Subscribe(T subscriber) được gọi, luồng đi vào phần khóa. và sau đó, khi mã bên trong khóa gọi phương thức AddToSubscribers(T subscriber), phương thức có khóa khác. tại sao khóa thứ hai này lại cần thiết?lý do đằng sau khóa bên trong khóa?

public abstract class SubscriptionManager<T> where T : class 
{ 
    private static List<T> subscribers; 
    private static void AddToSubscribers(T subscriber) 
    { 
     lock (typeof(SubscriptionManager<T>)) 
     { 
     if (subscribers.Contains(subscriber)) 
      return; 
     subscribers.Add(subscriber); 
     } 
    } 

    public void Subscribe(T subscriber) 
    { 
     lock (typeof(SubscriptionManager<T>)) 
     { 
     AddToSubscribers(subscriber); 
     } 
    } 
} 

Trả lời

10

Trong ngữ cảnh đó, không phải; tuy nhiên, kể từ khi khóa được tái tham gia có thể hữu ích để đảm bảo rằng bất kỳ người gọi khác của AddToSubscribers quan sát khóa. Trên thực tế, vì lý do đó tôi muốn nói "xóa nó khỏi Subscribe và chỉ để AddToSubscribers thực hiện khóa".

Tuy nhiên! Khóa trên Type khá nguy hiểm. Một trường sẽ an toàn hơn:

// assuming static is correct 
private static readonly object syncLock = new object(); 

lock(syncLock). Tùy thuộc vào thời điểm subscribers được chỉ định, bạn cũng có thể nhận được ngay với lock(subscribers) (và không có trường bổ sung).

Tôi cũng nên lưu ý rằng có phương thức dụ thêm vào trạng thái tĩnh là khá .... bất thường; IMO Subscribe phải là phương pháp static, vì nó không liên quan gì đến phiên bản hiện tại.

+0

thực sự, ví dụ này xuất phát từ một cuốn sách WCF, và lớp SubscriptionManager là lớp cơ sở cho dịch vụ WCF các lớp học. do đó các phương thức thể hiện được yêu cầu. Tôi cũng đọc từ MSDN rằng khóa trên một loại là nguy hiểm như bạn đã nêu, thực tế là bối cảnh là WCF làm cho bất kỳ sự khác biệt? – Yeonho

+1

@Daniel không gì cả. –

+0

@Marc "đối tượng đọc riêng tĩnh syncLock = new object();" - điều này cho một khóa toàn cầu duy nhất cho tất cả SubscriptionManager ; trong khi tôi tin rằng mã ban đầu có một khóa cho mỗi loại được xây dựng (tức là một khóa cho mỗi "T" riêng biệt). – Joe

4

Trong mã bạn đã đăng, không cần thiết. Nhưng sau đó một lần nữa, mã bạn đăng không đầy đủ - ví dụ danh sách người đăng ký không bao giờ được khởi tạo.

Khóa trên typeof (SubscriptionManager) có lẽ không phải là một ý kiến ​​hay - khóa trên trường subscribers sẽ tốt hơn - nhưng sẽ yêu cầu trường đăng ký được khởi tạo, ví dụ:

private static List<T> subscribers = new List<T>(); 
2

Có thể bạn nên đọc gần mẫu đó và xem sách nói về điều gì.

Đối với trường hợp cụ thể đó - không, khóa thứ hai là không cần thiết.

Lưu ý: Mẫu nguy hiểm vì nó khóa trên vật thể công cộng (loại). Thông thường một ổ khóa trên đối tượng riêng tư đặc biệt để mã bên ngoài không thể nhầm lẫn giới thiệu deadlocks bằng cách nhầm lẫn khóa trên cùng một đối tượng.

1

Tôi cũng phải đối mặt với tình huống khi đã sử dụng Khóa lồng nhau.

Trường hợp của tôi là, chức năng của khóa thứ hai có thể được gọi từ nơi khác, vì đó là chức năng tĩnh. Tuy nhiên, đối với trường hợp của bạn, nó sẽ không cần thiết vì mỗi thành viên dữ liệu thuộc về Instance và không tĩnh.

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