2017-01-29 4 views
0

Ich muss den Aufgabenaufrufen in einer mobilen App eine Zeitüberschreitungsfunktion hinzufügen. Ich habe versucht, dies mithilfe von Task.WhenAny wie unten gezeigt zu beenden. Dies gibt die Aufgabe zurück, die zuerst beendet wird. Mein Problem ist, dass ich ursprünglich den Rückgabewert von dieser Aufgabe erhielt, und ich brauche es immer noch, wenn die Aufgabe nicht abgelaufen ist.So erhalten Sie das Ergebnis einer Aufgabe, wenn Sie Task.WhenAny verwenden, um eine Zeitüberschreitung zu berücksichtigen

Task task = restService.GetRequestAsync(articleAdr, articleParams); 
var response = await Task.WhenAny(task, Task.Delay(1000, cts.Token)); 

Antwort ist einfach die Aufgabe, die zuerst abgeschlossen wurde. Wie bekomme ich das Ergebnis?

Antwort

1

Eine Frage jedoch, wie ist Ihre CancellationTokenSource erstellt und initialisiert und wann rufen Sie Cancel darauf an?

Am besten wäre, wenn Ihre Methode GetRequestAsync eine CancellationToken akzeptieren würde. Ziehen Sie das immer vor, wenn möglich, da Sie eine CancellationTokenSource erstellen können, die nach einer festgelegten Periode einen Abbruch auslöst. Würde dich den Anruf auf Task.WhenAny speichern.

In der Regel gibt es mehrere Optionen, wird es nachstehend beschrieben:

// Set timeout of 1 second 
CancellationTokenSource cts = new CancellationTokenSource(); 
cts.CancelAfter(TimeSpan.FromSeconds(1)); 

... 

Task task = restService.GetRequestAsync(articleAdr, articleParams); 

// Wait until timeout or request is done. 
await Task.WhenAny(task, Task.Delay(TimeSpan.FromMilliseconds(-1), cts.Token)); 

// If the cancellation is not yet requested the request was done before the timeout was reached 
if(!cts.IsCancellationRequested) 
{ 
    var response = await task; 
} 

Another Optionen ist dies:

Task requestTask = restService.GetRequestAsync(articleAdr, articleParams); 
var firstCompletedTask = await Task.WhenAny(requestTask, Task.Delay(1000, cts.Token)); 
if(firstCompletedTask == requestTask) 
{ 
    cts.Cancel(); // optionally, will cancel the delay task since it is of no use any more. 
    var response = await requestTask; 
} 

Eine erledigte Aufgabe so oft gewartet werden können, wie Sie wollen, und es wird immer das gleiche Ergebnis liefern.

0

Ich kann mir drei verschiedene Möglichkeiten für dieses Szenario vorstellen. Die ersten beiden können in Peter Bons' answer gefunden werden.

Der dritte speichert Ihre zwei Aufgaben ab und überprüft dann den Status, nachdem die await Task.WhenAny() abgeschlossen ist.

var workerTask = restService.GetRequestAsync(articleAdr, articleParams); 
var cancellationTask = Task.Delay(1000, cts.Token); 

await Task.WhenAny(workerTask, cancellationTask); 
if (workerTask.Status == TaskStatus.RanToCompletion) 
{ 
    // Note that this is NOT a blocking call because the Task ran to completion. 
    var response = workerTask.Result; 

    // Do whatever work with the completed result. 
} 
else 
{ 
    // Handle the cancellation. 
    // NOTE: You do NOT want to call workerTask.Result here. It will be a blocking call and will block until 
    // your previous method completes, especially since you aren't passing the CancellationToken. 
} 
1

Ich denke, Sie können einen Blick auf @Jamesmontemagno MVVM Helpers werfen. Es gibt eine extesion, dass Sie ein Timeout auf eine Aufgabe

MVVM Helpers - Utils

hier hinzufügen können Sie ein Video finden können, wo James erklären, wie es

The-Xamarin-Show-12-MVVM-Helpers

zu benutzen (in der Nähe von 26:38 Minuten)

0

Ich muss den Aufgabenaufrufen in einer mobilen App eine Zeitüberschreitungsfunktion hinzufügen. Ich habe versucht, dies mithilfe von Task.WhenAny wie unten gezeigt zu beenden.

Zunächst sollten Sie sich bewusst sein, dass sie nicht die CancellationToken-GetRequestAsync vorbei, sie ist nicht wirklich die Anfrage abgebrochen wird. Es wird weiter verarbeitet.

Zweitens finde ich Ihren Code ziemlich seltsam, da es in seinem aktuellen Zustand zwei Timeouts möglich ist: die Task.Delay vervollständigen kann, oder die CancellationToken signalisiert werden kann. Eine davon (Task.Delay) ist eine normale Aufgabenerfüllung und die andere (CancellationToken) ist eine echte Löschung.

Wenn die CancellationTokenist Ihr Timeout, dann können Sie die WaitAsync Methode aus meiner Nito.AsyncEx.Tasks Bibliothek verwenden:

Task task = restService.GetRequestAsync(articleAdr, articleParams); 
await task.WaitAsync(cts.Token); 
var result = await task; 

Wenn die CancellationToken eine vom Nutzer gewünschte Stornierung ist, und dass die Task.Delay ist die Timeout Sie anwenden möchten, dann würde ich empfehlen, Ihre Timeout als eine andere Art von Annullierung Modellierung:

Task task = restService.GetRequestAsync(articleAdr, articleParams); 
using (var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token)) 
{ 
    timeoutCts.CancelAfter(1000); 
    await task.WaitAsync(timeoutCts.Token); 
} 
var result = await task; 

Wenn Sie nicht verwenden möchten Nito.AsyncEx.Tasks, dann die beste Wahl ist wahrscheinlich so etwas wie diese (unter der Annahme Task.Delay als Timeout bestimmt ist und CancellationToken ist ein Benutzer Stornierungsanfrag):

Task task = restService.GetRequestAsync(articleAdr, articleParams); 
var completed = await Task.WhenAny(task, Task.Delay(1000, cts.Token)); 
if (completed != task) 
    throw new OperationCanceledException(); 
var result = await task; 
Verwandte Themen