2012-04-16 27 views
7

Tôi có một tình huống mà, để thử nghiệm, tôi chỉ muốn phương pháp hẹn giờ của tôi (FooMethod) để chạy một tại một thời điểm. Trong ví dụ bên dưới, FooMethod được chuyển làm đại biểu cho bộ hẹn giờ. Có nhiều trường hợp cụ thể của lớp này. Tôi nghĩ rằng bằng cách làm cho _locker tĩnh, chỉ có một thể hiện của FooMethod() sẽ xử lý tại một thời điểm. Nhưng khi tôi chạy ứng dụng, nhiều luồng đang vượt qua dòng TryEnter() tại một thời điểm.Monitor.TryEnter với Generic Class

Đây là cách tôi thêm từng lớp vào bộ hẹn giờ mới. Này được thực hiện, trong vòng một, cho mỗi trường hợp foo:

_timers.Add(new Timer(foo.FooMethod, null, 0, 10000)); 

Và đây là lớp học mà có phương pháp đó:

public class Foo<T> 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 

Lưu ý: Thông thường, _locker không phải là tĩnh; Tôi không muốn cùng một thread vào phương thức trước khi nó có cơ hội hoàn thành. Tôi đã đổi nó thành tĩnh để thử nghiệm.

Suy nghĩ đầu tiên của tôi là có thể điều này không hiệu quả vì lớp học là chung chung? Và rằng mỗi lớp cụ thể thực sự là lớp riêng của nó và chúng không chia sẻ biến _locker? Điều đó có đúng không? Nếu đó là sự thật làm thế nào tôi nên có các lớp cụ thể chia sẻ một biến _locker? Tôi có cần phải thêm biến _locker tĩnh vào một số lớp khác mà Foos có quyền truy cập không?

+0

câu hỏi rất thú vị và được hỏi rõ. +1 – spender

+2

Lưu ý rằng cùng một chuỗi * có thể * nhập lại một khóa (nhưng chỉ khi có một số đệ quy, tất nhiên).Đó là những gì khóa được cho: ngăn chặn * khác nhau * chủ đề từ việc sử dụng cùng một nguồn tài nguyên. – phoog

+0

@spender Cảm ơn bạn. Và phoog, tôi hiểu. Mục tiêu của tôi với biến tĩnh là ngăn chặn nhiều luồng xử lý phương thức đó cùng một lúc. Nhưng điều đó không hiệu quả. –

Trả lời

7

Tôi có cần phải thêm biến _locker tĩnh vào một số lớp khác vào mà Foos có quyền truy cập không?

Có.

Mỗi loại đóng Foo<T>, với các đối số khác nhau T, có đối tượng _locker tĩnh riêng. Bạn có thể làm cho Foo kế thừa từ một lớp cơ sở, và đặt đối tượng tĩnh ở đó. Sau đó, tất cả các loại sẽ sử dụng cùng một trường hợp.

6

lẽ

public class Foo 
{ 
    protected static readonly object _locker = new object(); 
} 

public class Foo<T> : Foo 
{ 
    public void FooMethod(object stateInfo) 
    {   
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 
+0

Cảm ơn bạn. Đây là một minh họa tốt về một cách thanh lịch để giải quyết vấn đề này. –

0

hãy làm cho lớp này là loại không chung chung. Điều đó sẽ nhằm mục đích nhu cầu của bạn.

public class Foo 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 
+0

Nhưng tôi có lớp này là chung chung vì một lý do. Tôi không muốn hoàn tác điều đó. –

2

Bạn là chính xác. Mỗi loại duy nhất T được tham chiếu trong mã khiến CLR tạo ra loại bê tông mới cho Foo<T> và mỗi nhóm có một nhóm thành viên tĩnh riêng.

Bạn có thể cấu trúc lại mã của mình để trông giống như sau. Nó là một trong số nhiều biến thể hợp lệ.

public class Foo 
{ 
    private static readonly object _locker = new object(); 

    public void FooMethod(object stateInfo) 
    { 
     // Don't let threads back up; just get out 
     if (!Monitor.TryEnter(_locker)) { return; } 

     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      Monitor.Exit(_locker); 
     } 
    } 
} 

public class Foo<T> 
{ 
    public void FooMethod(object stateInfo) 
    { 
     Foo.FooMethod(stateInfo); 
    } 
} 

Ngoài ra, hãy nhớ rằng bạn có thể bắt đầu hẹn giờ với một vô hạn period để ngăn chặn việc gọi lại từ thực hiện nhiều hơn một lần. Hãy gọi lại Change vào cuối số FooMethod để xếp hàng lại bộ hẹn giờ. Vì bạn có nhiều bộ tính giờ tất cả sẽ xuất hiện một khi bạn vẫn sẽ có nhiều lần thực hiện đồng thời FooMethod đồng thời, nhưng ít nhất hiện tại sẽ chỉ có một cuộc gọi hiện hoạt trên mỗi bộ hẹn giờ. Đó không phải chính xác những gì bạn yêu cầu, nhưng tôi nghĩ tôi sẽ chỉ ra điều này.

_timers.Add(new Timer(foo.FooMethod, _timers.Count, 10000, Timeout.Infinite)); 

public class Foo<T> 
{ 
    public void FooMethod(object stateInfo) 
    { 
     try 
     { 
      // Logic here 
     } 
     finally 
     { 
      int index = (int)stateInfo; 
      _timers[index].Change(10000, Timeout.Infinite); 
     } 
    } 
} 
+0

Nội dung hay. Cảm ơn, Brian! –

+0

Nhân tiện, tôi nên chỉ ra rằng ví dụ thứ hai của tôi có tình trạng đua khá nghiêm trọng. 'FooMethod' có thể được gọi trước khi timer được thêm vào' List'. Không bao giờ thực tế tôi đang đọc và viết cho bộ sưu tập đó ở cùng một điều rõ ràng là không an toàn cho luồng. Rõ ràng là ví dụ thứ hai của tôi cần một số công việc bổ sung, nhưng bạn có được ý tưởng. –