2009-04-03 28 views
5

Tôi đang tìm một mục đích chung thử và thử lại với thời gian chờ trong C#. Về cơ bản, tôi muốn những điều sau đây:Mục đích chung Hãy thử và Thử lại với Thời gian chờ trong C#?

bool stopTrying = false; 
DateTime time = DateTime.Now; 
while (!stopTrying) 
{ 
    try 
    { 
     //[Statement to Execute] 
    } 
    catch (Exception ex) 
    { 
     if (DateTime.Now.Subtract(time).Milliseconds > 10000) 
     { 
      stopTrying = true; 
      throw ex; 
     } 
    } 
} 

Trong trường hợp trên, tôi đợi 10 giây, nhưng phải là thời gian chờ thay đổi dựa trên tham số. Tôi không muốn phải lặp lại mã đầy đủ này bất cứ nơi nào tôi cần sử dụng nó. Có nhiều nơi trong mã của tôi, nơi chúng không phải là một thời gian chờ được tích hợp vào API và tôi sẽ nhấn một ngoại lệ nếu ứng dụng chưa sẵn sàng cho câu lệnh thực thi. Điều này cũng sẽ tránh việc phải mã hóa sự chậm trễ trong ứng dụng của tôi trước khi có sự an toàn này.

Làm rõ: Tuyên bố được đề cập có thể giống như bài tập. Nếu tôi sử dụng một đại biểu và method.Invoke, không phải là invokation scoped bên trong đại biểu và không phải là phương pháp ban đầu?

Trả lời

15

Sử dụng ví dụ của bạn, giải pháp rất đơn giản:

bool DoOrTimeout<T>(T method, TimeSpan timeout) where T : delegate // FIXME 
{ 
    bool stopTrying = false; 
    DateTime time = DateTime.Now; 
    while (!stopTrying) 
    { 
     try 
     { 
      method.Invoke(); 
      stopTrying = true; 
     } 
     catch (Exception ex) 
     { 
      if (DateTime.Now.Subtract(time).Milliseconds > timeout.TotalMilliseconds) 
      { 
       stopTrying = true; 
       throw; 
      } 
     } 
    } 
} 

Chỉ cần gọi DoOrTimeout với một đại biểu như các tham số đầu tiên.

+8

Vui lòng sử dụng 'ném ; 'để xem xét lại các ngoại lệ, không phải là 'ném ex', vì sau này phá hủy stac k dấu vết. – Will

+0

@Vẫn, tôi đã sử dụng mã của OP. Bạn nói đúng, mặc dù; 'ném' nên được sử dụng một mình. Tôi sẽ cập nhật câu trả lời của tôi để phản ánh điều này. – strager

+0

Bạn đang thiếu một stopTrying = true; sau khi gọi "method.Invoke()"; –

0

Tạo phương thức có biểu thức lambda cho Tuyên bố để thực thi và tham số cho thời gian chờ. Bên trong phương thức đó thực thi biểu thức lambda bên trong khối try/catch và tham số sử dụng cho timeout.

1

Đó không phải là điều đẹp nhất, nhưng tôi có vẻ hoạt động tốt cho đến nay. Và nó không sử dụng ngoại lệ để chỉ ra thời gian chờ.

public static class TimeoutOperation 
{ 
    private static readonly TimeSpan DefaultTimeout = new TimeSpan(0, 0, 10); 
    private static readonly TimeSpan DefaultGranularity = new TimeSpan(0, 0, 0, 0, 100); 

    public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action) 
    { 
    return DoWithTimeout<TResult>(action, DefaultTimeout); 
    } 

    public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action, TimeSpan timeout) 
    { 
    return DoWithTimeout<TResult>(action, timeout, DefaultGranularity); 
    } 

    public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action, TimeSpan timeout, TimeSpan granularity) 
    { 
    Thread thread = BuildThread<TResult>(action); 
    Stopwatch stopwatch = Stopwatch.StartNew(); 
    ThreadResult<TResult> result = new ThreadResult<TResult>(); 

    thread.Start(result); 
    do 
    { 
     if (thread.Join(granularity) && !result.WasSuccessful) 
     { 
     thread = BuildThread<TResult>(action); 
     thread.Start(result); 
     } 

    } while (stopwatch.Elapsed < timeout && !result.WasSuccessful); 
    stopwatch.Stop(); 

    if (thread.ThreadState == System.Threading.ThreadState.Running) 
     thread.Abort(); 

    return result; 
    } 

    private static Thread BuildThread<TResult>(Func<TResult> action) 
    { 
    return new Thread(p => 
    { 
     ThreadResult<TResult> r = p as ThreadResult<TResult>; 
     try { r.Result = action(); r.WasSuccessful = true; } 
     catch (Exception) { r.WasSuccessful = false; } 
    }); 
    } 

    public class ThreadResult<TResult> 
    { 
    public TResult Result { get; set; } 
    public bool WasSuccessful { get; set; } 
    } 
} 
Cách sử dụng
var result = TimeoutOperation.DoWithTimeout<int>(() => 
    { 
    Thread.Sleep(100); 
    throw new Exception(); 
    }); 
result.WasSuccessful // = false 
result.Value // = 0 

var result = TimeoutOperation.DoWithTimeout<int>(() => 
    { 
    Thread.Sleep(2000); 
    return 5; 
    }); 
result.WasSuccessful // = true 
result.Value // = 5 
1

Hãy nhìn vào câu hỏi này. Những gì bạn yêu cầu là chính xác một trong những mục đích tôi dự định.
Implement C# Generic Timeout

CẢNH BÁO: Mẫu này sử dụng Thread.Abort. Thực hiện theo các liên kết đến câu hỏi ban đầu của tôi để đọc một vài cảnh báo về điều đó trong các ý kiến.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 

namespace Something 
{ 
    public static class TimeoutWrapper 
    { 
    public static void Invoke(TimeSpan timeout, Action action) 
    { 
     Invoke(timeout, action, null); 
    } 
    public static void Invoke(TimeSpan timeout, Action action, Action abort) 
    { 
     Thread threadToKill = null; 
     Action wrappedAction =() => 
     { 
     threadToKill = Thread.CurrentThread; 
     action(); 
     }; 

     IAsyncResult result = wrappedAction.BeginInvoke(null, null); 
     if (result.AsyncWaitHandle.WaitOne(timeout, true)) 
     { 
     wrappedAction.EndInvoke(result); 
     } 
     else 
     { 
     if (threadToKill != null) 
     { 
      try { threadToKill.Abort(); } 
      catch { /* Ignore */ } 
     } 

     if (abort != null) 
      abort(); 

     throw new TimeoutException(); 
     } 
    } 
    } 
} 

Chỉ cần chạy điều này trong vòng lặp có kiểm soát thời gian chờ thích hợp.

DateTime endAt = DateTime.Now.AddMinutes(1); 
Timespan timeout = new Timespan(0, 0, 0, 5); 
while(DateTime.Now < endAt) 
{ 
    try 
    { 
     TimeoutWrapper.Invoke(timeout,() => DoSomething()); 
     break; 
    } 
    catch(TimeoutException ex) 
    { /* Do something */ } 
} 
0

Mã này là sai (infinite loop):

if (DateTime.Now.Subtract(time).Milliseconds > 10000) 

Người bên phải là:

if (DateTime.Now.Subtract(time).TotalMilliseconds > 10000) 
0

Dưới đây là một giải pháp đơn giản:

long TIMEOUT = 60000; // 1 minute 
long INTERVAL = 1000; // 1 second 

System.DateTime startTime = System.DateTime.Now;  

while (check_condition()) 
{ 
    System.Threading.Thread.Sleep(INTERVAL); 
    long elapsedTime = System.DateTime.Now.Millisecond - startTime.Millisecond; 

    if (elapsedTime > TIMEOUT) 
    { 
     throw new Exception("Timeout exceeded"); 
    } 
} 
+0

bạn cũng có thể chỉ "phá vỡ" thay vì ném một ngoại lệ nếu bạn không quan tâm để xử lý nó. – fijiaaron

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