2008-11-24 31 views
6

Tại thời điểm này, tôi có một số chức năng mà trông như thế này:Tôi có thể thêm thuộc tính vào một hàm để ngăn chặn reentry không?

private bool inFunction1 = false; 
public void function1() 
{ 
    if (inFunction1) return; 
    inFunction1 = true; 

    // do stuff which might cause function1 to get called 
    ... 

    inFunction1 = false; 
} 

Tôi muốn để có thể khai báo như thế này:

[NoReEntry] 
public void function1() 
{ 
    // do stuff which might cause function1 to get called 
    ... 
} 

Có một thuộc tính tôi có thể thêm vào một chức năng để ngăn chặn reentry? Nếu không, làm thế nào tôi sẽ đi về làm một? Tôi đã nghe nói về AOP thuộc tính có thể được sử dụng để thêm mã trước và sau khi các cuộc gọi chức năng; họ có thích hợp không?

+0

Phạm vi của hạn chế này khi có nhiều chủ đề và nhiều phiên bản đối tượng là gì? Có phải chức năng 1 có thể được thực hiện bất kỳ lúc nào chỉ bằng một chuỗi đơn cho một cá thể đối tượng đơn lẻ hay nó thoải mái hơn? – Constantin

Trả lời

1

Không có thuộc tính nào được xác định trước. Bạn có thể tạo các thuộc tính mới, nhưng điều đó sẽ không giúp bạn. Vấn đề là làm cho thuộc tính tùy chỉnh ngăn chặn các phương pháp được gọi lại, mà tôi không nghĩ là doable.

Tuyên bố khóa không phải là những gì bạn muốn, vì điều đó sẽ làm cho các cuộc gọi chặn và chờ, không trả lại ngay lập tức.

PS: dùng thử ... cuối cùng sẽ chặn trong mẫu ở trên. Nếu không, nếu một ngoại lệ được ném vào giữa hàm, inFunction1 sẽ được giữ nguyên và tất cả các cuộc gọi sẽ trở lại ngay lập tức.

ví dụ: :

if (inFunction1) 
    return; 

try 
{ 
    inFunction1 = true; 

    // do stuff which might cause function1 to get called 
    ... 
} 
finally 
{ 
    inFunction1 = false; 
} 
0

Tôi không nghĩ điều đó là có thể.

Thuộc tính gần nhất sẽ là thuộc tính 'Đồng bộ hóa', nhưng điều đó sẽ chặn tất cả các cuộc gọi tiếp theo.

2

Bạn có thể thấy rằng bạn có thể sử dụng PostSharp để thực hiện việc này - cùng với các đề xuất từ ​​Anthony về việc sử dụng try/finally. Nó có thể là lộn xộn mặc dù. Ngoài ra, hãy cân nhắc xem bạn có muốn ủy thác lại trên cơ sở mỗi chủ đề hay từng trường hợp hay không. (Có thể nhiều chủ đề gọi vào phương thức để bắt đầu, hay không?)

Không có gì giống như thế này trong khung chính nó.

1

Nếu điều này là an toàn cho Chủ đề, bạn phải cẩn thận với biến đó.

Có thể một chuỗi khác có thể đi vào hàm và vượt qua kiểm tra trước khi chuỗi đầu tiên đã đặt biến.

Hãy chắc chắn rằng nó được đánh dấu dễ bay hơi như vậy:

private volatile bool inFunction1 = false; 
+3

"dễ bay hơi" sẽ không khắc phục tình trạng cuộc đua này. – Constantin

5

Nếu không có lắp ráp và IL viết lại, không có cách nào để bạn có thể tạo ra một thuộc tính tùy chỉnh mà sửa đổi mã theo cách bạn mô tả.

Tôi khuyên bạn nên sử dụng phương pháp dựa trên đại biểu thay thế, ví dụ: cho các chức năng của một cuộc tranh luận duy nhất:

static Func<TArg,T> WrapAgainstReentry<TArg,T>(Func<TArg,T> code, Func<TArg,T> onReentry) 
{ 
    bool entered = false; 
    return x => 
    { 
     if (entered) 
      return onReentry(x); 
     entered = true; 
     try 
     { 
      return code(x); 
     } 
     finally 
     { 
      entered = false; 
     } 
    }; 
} 

phương pháp này có chức năng để quấn (giả sử nó phù hợp với Func < Targ, T> - bạn có thể viết các biến thể khác, hoặc một phiên bản hoàn toàn chung với nỗ lực nhiều hơn nữa) và thay thế chức năng gọi trong trường hợp reentry. (Các chức năng thay thế có thể ném một ngoại lệ, hoặc quay trở lại ngay lập tức, vv) Sau đó, trong suốt mã của bạn, nơi bạn thường sẽ được gọi phương thức thông qua, bạn gọi đại biểu trả lại bởi WrapAgainstReentry() để thay thế.

+0

Điều này vẫn thể hiện các vấn đề cố hữu trong môi trường đa luồng. Xem bài đăng của tôi về các biến dễ bay hơi ở trên. –

+0

Tất nhiên, Rob. Nó chỉ là một bản demo, bởi vì nó cũng chỉ xử lý một chữ ký chức năng rất cụ thể. –

14

Thay vì sử dụng một bool và đặt nó trực tiếp, hãy thử sử dụng một chặng đường dài và lớp đan cài:

long m_InFunction=0; 

if(Interlocked.CompareExchange(ref m_InFunction,1,0)==0) 
{ 
    // We're not in the function 
    try 
    { 
    } 
    finally 
    { 
    m_InFunction=0; 
    } 
} 
else 
{ 
    // We're already in the function 
} 

Điều này sẽ làm cho thread kiểm tra an toàn.

+1

Ngoài sự tò mò, có lý do cụ thể nào để sử dụng 'long' thay vì' int' ở đây không? (Có quá tải 'CompareExchange (ref int, int, int)') – FunctorSalad

+2

Không có lý do cụ thể nào. Tôi sử dụng để làm rất nhiều lập trình Win32 và các chức năng khóa liên động có một thời gian dài. Sử dụng int sẽ không tạo ra bất kỳ sự khác biệt nào trong ví dụ trên. – Sean

+0

Bạn biết rằng trong C# từ lâu được định nghĩa là int64, phải không? – Theraot

4

Bạn có thể xây dựng một thuộc tính PostSharp để kiểm tra xem nếu tên của phương pháp này là trong ngăn xếp dấu vết hiện

[MethodImpl(MethodImplOptions.NoInlining)] 
    private static bool IsReEntry() { 
     StackTrace stack = new StackTrace(); 
     StackFrame[] frames = stack.GetFrames(); 

     if (frames.Length < 2) 
      return false; 

     string currentMethod = frames[1].GetMethod().Name; 

     for (int i = 2; i < frames.Length; i++) { 
      if (frames[i].GetMethod().Name == currentMethod) { 
       return true; 
      } 
     } 

     return false; 
    } 
+0

Điều đó sẽ chỉ hoạt động nếu reentry xảy ra trên cùng một sợi - nhưng tất cả như nhau, có vẻ thú vị. – Simon

+0

Tôi sẽ chỉ xem xét reentry để được trên cùng một sợi. Nếu reentry có nghĩa là trên các chủ đề, những gì về trên các lĩnh vực ứng dụng? – Bob

1

chủ đề này là một chút cũ nhưng tôi figured nó muốn được giá trị đưa nó vào 2012 bởi vì vấn đề này vẫn còn tồn tại (nhiều hơn hoặc ít hơn). Tôi đã có thể giải quyết vấn đề này bằng cách sử dụng các đối tượng proxy được tạo bằng cách sử dụng Reflection.Emit (cụ thể là sử dụng LinFu.DynamicProxy). Bài viết của LinFu cũ hơn bài viết này vì vậy tôi cho rằng mọi thứ mà nó thảo luận có liên quan khi nó được hỏi (và bằng cách nào đó vẫn còn ngày hôm nay).

Tôi đã sử dụng LinFu vì tôi đã sử dụng nó cho các mục đích khác, nhưng tôi chắc chắn một số khung công tác DynamicProxy khác có sẵn cho bạn (ví dụ Castle.DynamicProxy) hoặc bạn có thể tự cuộn dựa trên Reflection.Emit (không dành cho những người có sự phân tán yếu). Họ cung cấp một cơ chế lấp đầy một phần lớn vai trò của AOP trong khi vẫn giữ cho bạn kiểm soát mã của bạn.

0

Bạn có thể muốn xem xét tránh tái cấp phép bằng cách sửa đổi thiết kế của bạn để nó sẽ không bao giờ gọi hàm 1() trước khi yêu cầu trước đó của nó hoàn tất. Với tôi có vẻ như một lớp bị thiếu từ hàm trên1().

+0

Ok @Rob Tôi có thể đánh giá cao điều đó. – Chris

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