2014-05-05 50 views
7

Chcę uruchomić operację, która powinna przekroczyć limit czasu po n milisekundach. Zaimplementowałem to na dwa sposoby, jeden poprzez anulowanie operacji samemu po odczekaniu n milisekund i jeden przez przekazanie zestawu CancellationToken, aby wygasł po n milisekundach.timeout cancellationtoken vs task.delay() i timeout

Obawiam się, że ponieważ mój system jest obciążony dużym obciążeniem, sygnał anulujący może wygasnąć przed rozpoczęciem operacji. Wygląda na to, że jeśli sam wykonam timeout za pomocą Task.Delay(), wówczas wywołanie opóźnienia() nie będzie działać, dopóki nie zacznie się moja operacja.

Oto jak ja to robię:

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout) 
    { 
     Task completedTask = await Task.WhenAny(task, Task.Delay(timeout)); 
     if (completedTask == task) 
     { 
      return await task; 
     } 

     throw new TimeoutException(); 
    } 

// Use it like this 
await SomeOperationAsync().TimeoutAfter(TimeSpan.FromMilliseconds(n)); 

porównaniu do:

CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMilliseconds(n)); 
await SomeOperationAsync(source.Token); 
+0

To jest jedno duże obciążenie, aby wygasić źródło anulowania przed rozpoczęciem operacji. – Paparazzi

Odpowiedz

15

nie jestem pewien, że problemem jest to uzasadnione, ale mam zamiar założyć, że jest to .

Problem z kodem korzystającym z Task.Delay() polega na tym, że faktycznie nie anulujesz operacji. Może to oznaczać, że marnujesz zasoby lub wprowadzasz w błąd swoich użytkowników (mówisz im, że operacja przekroczyła limit czasu, podczas gdy operacja jest nadal uruchomiona i najprawdopodobniej zakończy się pomyślnie).

Teraz, jeśli chcesz, aby upewnić się, że token anulowanie zaczyna odliczać dopiero po zakończeniu operacji zostanie uruchomiona, wówczas zrobić:

var source = new CancellationTokenSource(); 
var task = SomeOperationAsync(source.Token); 
source.CancelAfter(TimeSpan.FromMilliseconds(n)); 
await task; 

Jeśli zrobisz to często, możesz chcieć ująć ten logiki do metody (może wymagać lepszej nazwy):

public static async Task WithTimeoutAfterStart(
    Func<CancellationToken, Task> operation, TimeSpan timeout) 
{ 
    var source = new CancellationTokenSource(); 
    var task = operation(source.Token); 
    source.CancelAfter(timeout); 
    await task; 
} 

Zastosowanie:

await WithTimeoutAfterStart(
    ct => SomeOperationAsync(ct), TimeSpan.FromMilliseconds(n));