2012-04-13 18 views
7

Jestem nowy do C#; Zrobiłem głównie Javę.Wdrażanie limitu czasu w C#

Chcę zaimplementować limitu czasu coś wzdłuż linii:

int now= Time.now(); 
while(true) 
{ 
    tryMethod(); 
    if(now > now+5000) throw new TimeoutException(); 
} 

Jak mogę wdrożyć to w C#? Dzięki!

+0

będziemy chcieli patrzeć na [Stoper] (http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx) klasy. [Tutaj] (http://stackoverflow.com/a/5019172/95573) to przykład: – SwDevMan81

+2

Co robi "tryMethod"? W powyższym kodzie twoje polecenie if zostanie osiągnięte dopiero po zakończeniu "trymethod". – JMK

+2

To wydaje się być czymś, co możesz chcieć wykonać asynchronicznie? – emd

Odpowiedz

25

Jedną z możliwości byłoby

Stopwatch sw = new Stopwatch(); 
sw.Start(); 
while(true) 
{ 
    tryMethod(); 
    if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException(); 
} 

Jednak obecnie nie ma sposobu, aby wydostać się z twojej pętli. Polecam mając tryMethod zwraca wartość logiczną i zmienić go na

Stopwatch sw = new Stopwatch(); 
sw.Start(); 
while(!tryMethod()) 
{ 
    if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException(); 
} 
+2

Występują problemy, jeśli chodzi o dni letniego oszczędzania. – Servy

+1

@Servy, masz rację, Zmieniono moją odpowiedź na stoper zamiast DateTime. –

+0

Jestem dość pewny, że OP chce mieć możliwość wykonania operacji timeout na tryMethod chociaż – KingCronus

4

Myślę, że można to zrobić z zegarem i delegata, mój przykładowy kod znajduje się poniżej:

using System; 
using System.Timers; 

class Program 
{ 
    public delegate void tm(); 

    static void Main(string[] args) 
    { 
     var t = new tm(tryMethod); 
     var timer = new Timer(); 
     timer.Interval = 5000; 

     timer.Start(); 

     timer.Elapsed += (sender, e) => timer_Elapsed(t); 
     t.BeginInvoke(null, null); 
    } 

    static void timer_Elapsed(tm p) 
    { 
     p.EndInvoke(null); 
     throw new TimeoutException(); 
    } 

    static void tryMethod() 
    { 
     Console.WriteLine("FooBar"); 
    } 
} 

Masz tryMethod, ty następnie utwórz delegata i skieruj tego delegata na tryMethod, a następnie uruchom Asynchronicznie tego delegata. Następnie masz zegar, z interwałem wynoszącym 5000 ms, przekazujesz swojego delegata do używanej przez ciebie metody zegarowej (która powinna działać jako delegat, jest typem odniesienia, a nie typem wartości) i po upływie 5000 sekund wywołujesz EndInvoke metoda na swoim delegacie.

+0

Myślę, że jest to przeciwieństwo tego, co chce OP - kod ten zapewnia, że ​​metoda nie kończy się na co najmniej 5000 ms, wygląda na to, że OP chce wielokrotnie uruchamiać metodę dla maksymalnie 5000 ms. –

+0

Ah przepraszam, mam to do tyłu! – JMK

+0

Druga odpowiedź (wersja z pętlą while) nie działa z blokowaniem. Zrobię kilka testów, ale po prostu prosząc o teraz, czy ten czasomierz podejmie pracę w celu zablokowania? – liang

2

Dopóki tryMethod() nie blokuje to powinien robić to, co chcesz:

nie jest bezpieczne dla czasu letniego lub zmiany stref czasowych, kiedy telefony: czas

DateTime startTime = DateTime.Now; 

while(true) 
{ 
    tryMethod(); 
    if(DateTime.Now.Subtract(startTime).TotalMilliseconds > 5000) 
     throw new TimeoutException(); 
} 

strefę czasową, a światło dzienne oszczędności bezpieczne wersje:

DateTime startTime = DateTime.UtcNow; 

while(true) 
{ 
    tryMethod(); 
    if(DateTime.UtcNow.Subtract(startTime).TotalMilliseconds > 5000) 
     throw new TimeoutException(); 
} 

(. NET 3.5 lub nowszej dla datetimeoffset)

DateTimeOffset startTime = DateTimeOffset.Now; 

while(true) 
{ 
    tryMethod(); 
    if(DateTimeOffset.Now.Subtract(startTime).TotalMilliseconds > 5000) 
     throw new TimeoutException(); 
} 
+0

Czy lepiej jest przekształcić czas DateTime na Universal, aby uwzględnić różne strefy czasowe? Również jeśli używam UtcNow, czy to by miało jakąkolwiek różnicę? –

+0

@SimpleFellow Istnieją dwa sposoby, aby zapewnić bezpieczeństwo strefy czasowej i czasu letniego. Zobacz zaktualizowaną odpowiedź. Używanie UtcNow jest lepsze niż konwersja, ponieważ konwersja może nadal mieć problemy w zależności od tego, kiedy wykonywana jest konwersja. – JamieSee

0

Innym sposobem Lubię to zrobić:

public class TimeoutAction 
    { 
     private Thread ActionThread { get; set; } 
     private Thread TimeoutThread { get; set; } 
     private AutoResetEvent ThreadSynchronizer { get; set; } 
     private bool _success; 
     private bool _timout; 

     /// <summary> 
     /// 
     /// </summary> 
     /// <param name="waitLimit">in ms</param> 
     /// <param name="action">delegate action</param> 
     public TimeoutAction(int waitLimit, Action action) 
     { 
      ThreadSynchronizer = new AutoResetEvent(false); 
      ActionThread = new Thread(new ThreadStart(delegate 
      { 
       action.Invoke(); 
       if (_timout) return; 
       _timout = true; 
       _success = true; 
       ThreadSynchronizer.Set(); 
      })); 

      TimeoutThread = new Thread(new ThreadStart(delegate 
      { 
       Thread.Sleep(waitLimit); 
       if (_success) return; 
       _timout = true; 
       _success = false; 
       ThreadSynchronizer.Set(); 
      })); 
     } 

     /// <summary> 
     /// If the action takes longer than the wait limit, this will throw a TimeoutException 
     /// </summary> 
     public void Start() 
     { 
      ActionThread.Start(); 
      TimeoutThread.Start(); 

      ThreadSynchronizer.WaitOne(); 

      if (!_success) 
      { 
       throw new TimeoutException(); 
      } 
      ThreadSynchronizer.Close(); 
     } 
    } 
1

Korzystanie Zadania dla niestandardowego limit czasu na metody Async

Oto moja implementacja niestandardowej klasy z metodą zawinąć zadanie mieć timeout .

public class TaskWithTimeoutWrapper 
{ 
    protected volatile bool taskFinished = false; 

    public async Task<T> RunWithCustomTimeoutAsync<T>(int millisecondsToTimeout, Func<Task<T>> taskFunc, CancellationTokenSource cancellationTokenSource = null) 
    { 
     this.taskFinished = false; 

     var results = await Task.WhenAll<T>(new List<Task<T>> 
     { 
      this.RunTaskFuncWrappedAsync<T>(taskFunc), 
      this.DelayToTimeoutAsync<T>(millisecondsToTimeout, cancellationTokenSource) 
     }); 

     return results[0]; 
    } 

    public async Task RunWithCustomTimeoutAsync(int millisecondsToTimeout, Func<Task> taskFunc, CancellationTokenSource cancellationTokenSource = null) 
    { 
     this.taskFinished = false; 

     await Task.WhenAll(new List<Task> 
     { 
      this.RunTaskFuncWrappedAsync(taskFunc), 
      this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource) 
     }); 
    } 

    protected async Task DelayToTimeoutAsync(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource) 
    { 
     await Task.Delay(millisecondsToTimeout); 

     this.ActionOnTimeout(cancellationTokenSource); 
    } 

    protected async Task<T> DelayToTimeoutAsync<T>(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource) 
    { 
     await this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource); 

     return default(T); 
    } 

    protected virtual void ActionOnTimeout(CancellationTokenSource cancellationTokenSource) 
    { 
     if (!this.taskFinished) 
     { 
      cancellationTokenSource?.Cancel(); 
      throw new NoInternetException(); 
     } 
    } 

    protected async Task RunTaskFuncWrappedAsync(Func<Task> taskFunc) 
    { 
     await taskFunc.Invoke(); 

     this.taskFinished = true; 
    } 

    protected async Task<T> RunTaskFuncWrappedAsync<T>(Func<Task<T>> taskFunc) 
    { 
     var result = await taskFunc.Invoke(); 

     this.taskFinished = true; 

     return result; 
    } 
} 

Następnie można nazwać tak:

await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000,() => this.MyTask()); 

lub

var myResult = await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000,() => this.MyTaskThatReturnsMyResult()); 

I można dodać znak anulowania, jeśli chcesz, aby anulować uruchomione zadanie asynchronicznej jeśli dojdzie do koniec czasu.

Nadzieja pomaga