2010-02-09 32 views
12

Tôi đã nhận thấy vấn đề này xảy ra rất nhiều trong hầu hết mọi thứ tôi làm, vì vậy tôi nghĩ rằng phải có một mẫu thiết kế cho việc này.Hãy thử, bắt vấn đề

Về cơ bản, nếu ngoại lệ được ném, hãy cố giải quyết sự cố và thử lại. Nếu tôi đặt nó trong thử, tất cả nó sẽ làm là bắt ngoại lệ, nhưng tôi muốn thử lại bất cứ điều gì nó đã làm và nếu nó không thành công một lần nữa, thử lại một số lần nhất định.

Có mẫu chung cho loại nội dung này không?

+1

một vòng lặp có điều kiện? nếu bạn không xử lý ngoại lệ, tại sao bạn đặt nó trong một thử/bắt – davidosomething

+5

Sự điên rồ đang làm điều tương tự hai lần và mong đợi các kết quả khác nhau. Tôi nói rằng nếu bạn nhận được một ngoại lệ, hầu như luôn luôn điều tốt nhất để làm là để nói cho người dùng và để cho * họ * giải quyết vấn đề. –

+3

@Eric Tôi đoán anh ấy đang làm điều gì đó như chạy một dịch vụ kết nối với một dịch vụ khác. Nếu kết nối không thành công, nó sẽ ném và ngoại lệ. Sau đó, anh ấy muốn thử lại kết nối vì có thể có một số lý do không liên quan đến mã của riêng anh ấy cho sự cố (máy chủ, mạng xuống, trục trặc định tuyến, v.v.). Thông báo cho người dùng là loại ngớ ngẩn, vì người dùng sẽ chỉ làm điều tương tự, cố gắng kết nối lại. –

Trả lời

17

việc kiểm tra này SO trả lời .. hy vọng rằng sẽ giúp u

Cleanest way to write retry logic?

public static class RetryUtility 
{ 
    public static void RetryAction(Action action, int numRetries, int retryTimeout) 
    { 
     if(action == null) 
      throw new ArgumenNullException("action"); 

     do 
     { 
      try 
      { 
       action(); 
       return; 
      } 
      catch 
      { 
       if(numRetries <= 0) 
        throw; // Avoid silent failure 
       else 
       { 
        Thread.Sleep(retryTimeout); 
        numRetries--; 
       } 
      } 
     } 
     while(numRetries > 0); 
    } 
} 

Gọi

RetryUtility.RetryAction(() => SomeFunctionThatCanFail(), 3, 1000); 

tín dụng đi vào LBushkin

+3

+1: Cái này nhận được ý kiến ​​của tôi. Đó là loại đại biểu của những người sống. –

+1

Tôi chuẩn bị trả lời câu trả lời thứ hai và nói, "Chà, tôi chỉ tóm tắt nó thành một phương pháp lấy một đại biểu và thêm một bộ đếm thử lại .." Sau đó tôi cuộn xuống. Câu trả lời tuyệt vời. –

+0

-1: việc bắt giữ chăn không có ý nghĩa gì, ngay cả khi nó chỉ xảy ra lần 'numRetries'. Bạn không có ý tưởng ngoại lệ được ném, hoặc cho dù nó có ý nghĩa để thử lại không. –

2

thử/bắt trong vòng lặp, với bộ đếm để thử lại? EDIT: Và yêu cầu của bạn về "thử lại bất cứ điều gì nó đã làm", bạn cần logic tùy chỉnh cho điều đó, làm thế nào để thử lại thay đổi một cách hoang dại (tức là, mở lại một dòng, tạo lại đối tượng, tạm dừng cho X mili giây, vv ...), vì vậy bạn cần thử/nắm bắt riêng bên trong vòng lặp cho mọi hoạt động nguyên tử.

Bằng "hoạt động nguyên tử", tôi có nghĩa là một tập hợp các câu lệnh liên quan, chẳng hạn như đọc tệp. Toàn bộ tập tin đọc vào bộ nhớ có thể là một hoạt động nguyên tử, ví dụ.

2

Trên một số cơ sở hạn chế, bạn có thể muốn đặt thử/nắm bắt thành vòng lặp và buộc ngắt nếu thành công cuối cùng. Như vậy có thể là để kiểm tra truy cập internet và bạn muốn người dùng có một nỗ lực khác khi kết nối.

6

này chạy indefinately nhưng nó sẽ dễ dàng quảng cáo d truy cập vòng lặp đến khoản tiền trong khi

var solved = false; 
    var tries = 0; 

    while (!solved) 
    { 
     try 
     { 
      //Do Something 
      solved = true; 
     } 
     catch 
     { 
      //Fix error 
     } 
     finally 
     { 
       if(solved || IsRediculous(tries)) 
       break; 

       tries++; 
     } 
    } 
+1

Điều đó sẽ lặp lại mãi mãi! – Dolph

+1

@Dolph Mathews: Tất nhiên! Nếu lúc đầu bạn không thành công ... –

+0

Có, nó sẽ lặp mãi mãi cho đến khi nó được giải quyết = true; – Pauk

1

Có, rất phổ biến khi có một số lần thử lại khi bạn thoát khỏi vòng lặp thành công. Một vài điều:

Bạn có thể muốn thêm độ trễ trước khi thử lại để bạn không sử dụng hết tất cả các lần thử lại chỉ trong vài phần nghìn giây trước khi sự cố tạm thời có thời gian để tự khắc phục.

Nếu cuối cùng bạn thất bại, bạn nên ném ngoại lệ đầu tiên bạn bị bắt, chứ không phải trường hợp ngoại lệ đầu tiên. Ngoại lệ thứ hai có thể là kết quả của việc không phục hồi chính xác từ lỗi đầu tiên và có thể không giúp gỡ lỗi vấn đề ban đầu.

2

Something như thế này, có lẽ:

int MAX_RETRIES = 5; 
for (var attempt=1; attempt <= MAX_RETRIES; attempt++) { 
    try { 
     DoSomethingThatMightThrow(); 
    } 
    catch (AnExceptionIKnowHowToHandle) { 
     if (attempt < MAX_RETRIES) 
      continue; 

     throw; 
    } 
} 
+1

Mark làm cho một điểm tốt trong câu trả lời của mình. Tùy thuộc vào kịch bản bạn có thể muốn lưu trữ một tham chiếu đến ngoại lệ ban đầu và sau đó lại ném nó (hoặc chỉ ném ngoại lệ cuối cùng, vượt qua một bản gốc như InnerException) nếu bạn vượt quá thử lại tối đa của bạn.Ngoài ra, như Mark đã nói, bạn có thể muốn thêm sự chậm trễ giữa các lần thử lại nếu ngoại lệ dự kiến ​​của bạn có thể do tranh chấp tài nguyên gây ra. –

1

Phụ thuộc những gì bạn đang cố gắng, nhưng thông thường bạn muốn kiểm tra khả năng của một ngoại lệ xảy ra trước khi thực thi mã mà có thể gây ra một ngoại lệ.

Ví dụ: kiểm tra xem tệp có tồn tại trước khi truy cập và tạo tệp đó (hoặc bất kỳ tệp nào) nếu không.

+0

File.Exists có thể trả về false nếu tệp không tồn tại hoặc nếu không thể truy cập tệp. Bạn sẽ không bao giờ biết cái nào trong số đó là trừ khi bạn cố gắng mở tệp. Bằng cách đó, bạn cũng sẽ nhận được một ngoại lệ với các chi tiết về lý do tại sao các tập tin không thể được mở (ít nhất, bạn sẽ nếu bạn sử dụng ex.ToString()). –

1

Bạn có chắc chắn xử lý ngoại lệ là phương pháp phù hợp ở đây không?Nếu bạn có thể "giải quyết vấn đề", bạn có thể phát hiện tình trạng lỗi trước khi gọi mã ngoại lệ-generatiing.

Xử lý ngoại lệ là điều tự nhiên nhất đối với những điều thực sự vượt trội. Kết nối Internet không thành công (như trong câu trả lời trước) là nội dung có thể được phát hiện và xử lý trước mã ném ngoại lệ gọi.

+0

Ngoại trừ kết nối internet có thể bắt đầu thất bại sau khi bạn đã cố gắng phát hiện và trước khi bạn sử dụng nó. –

1

Mã hóa những gì người khác đã đề cập:

var success = false; 
var attempts = 0; 
var maxAttempts = 0; 

do { 
    attempts++; 

    try { 
    /* your code */ 
    success = condition; 
    } catch(SuperciliousException e) { 
    /* recover */ 
    } 
} while(!success && attempts < maxAttempts); 
+0

Đây là giải pháp tốt hơn, vì bạn đang nỗ lực. Tuy nhiên, người dùng nên hiểu ngoại lệ là gì và cố gắng ngăn chặn hoặc loại trừ ngoại lệ đó. +1 cho những nỗ lực. –

+0

@Mathews: Không có 'Integer' trong C#. Nó là 'int' hoặc' Int32'. – missingfaktor

+0

Tôi đã viết điều này trong Java, hãy chỉnh sửa thành C# – Dolph

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