2016-09-23 6 views
0

Ich versuche, einige (in meinen Augen) seltsames Verhalten zu verstehen. Ich habe eine asynchrone Methode aufgerufen und möchte das Ergebnis abrufen. (DeleteIndexAsync zurückgeben Task<bool>)Versucht, Task.ContinueWith() zu verstehen

var deleteTask = Task.Run(() => DeleteIndexAsync(localItem)) 
    .ContinueWith(t => 
    { 
    //handle and log exceptions  
    }, TaskContinuationOptions.OnlyOnFaulted); 

if (!deleteTask.Result) 

In diesem Szenario Result ist false und die Status ist WaitingForActivation.

Während dieser Code tut, was ich will

var deleteTask = Task.Run(() => DeleteIndexAsync(localItem)); 
    deleteTask.ContinueWith(t => 
    { 
    //handle and log exceptions 
    return false; 
    }, TaskContinuationOptions.OnlyOnFaulted); 

    if (!deleteTask.Result) 

Kann jemand erklären, warum? Und ist es möglich, async/await statt Task hier zu verwenden?

Edit:

var deleteTask = Task.Run(() => ThrowEx()); 
    bool errorOccurred = false; 
    deleteTask.ContinueWith(t => 
    {   
    errorOccurred = true; 
    }, TaskContinuationOptions.OnlyOnFaulted); 

    if (errorOccurred) 
    { 
    return true; 
    } 

Antwort

2

Wenn Sie die Anrufe verketten, wie im ersten Beispiel, der Wert, den Sie in die deleteTask Variable zuweisen ist eigentlich die zweite Task. Dies ist derjenige, der nur bei einem Ausfall der ersten Task ausgeführt werden soll (derjenige, der DeleteIndexAsync aufruft).

Dies liegt daran, dass sowohl Task.Run als auch Task.ContinueWithTask s zurückgeben, die sie erstellen. Es erklärt, warum Sie im ersten Beispiel Status == WaitingForActivation erhalten. Im ersten Snippet würde der Zugriff auf deleteTask.Result eine Ausnahme auslösen. Wenn DeleteIndexAsync warf, wäre es eine AggregateException, die die ursprüngliche Ausnahme enthält (außer Sie haben auf t.Exception zugegriffen), andernfalls würde es bedeuten, dass die "Operation abgebrochen wurde" - das ist, weil Sie versuchen, das Ergebnis der Aufgabe zu erhalten, die bedingt geplant wurde Die Bedingung wurde nicht erfüllt.

Wenn Sie Verfahren hergestellt ist, die die snipped async Sie es so tun konnte (nicht getestet):

bool success = false; 
try 
{ 
    success = await DeleteIndexAsync(localItem); 
} 
catch (Exception) {} 

if (!success) 
{ 
    //TODO: handler 
} 

Zu Frage bearbeiten:

erfasst Variable sollte helfen, aber Ihre aktuelle Lösung führt Race Condition ein. In diesem Fall wäre es besser, wie dies zu tun:

var deleteTask = Task.Run(() => ThrowEx());  

try 
{ 
    deleteTask.Wait(); 
} 
catch (Exception ex) 
{ 
    return true; 
} 

aber an diesem Punkt können Sie den asynchronen Aufruf ganz fallen lassen, weil man auf das Ergebnis warten sofort - es sei denn, dieses Beispiel Arbeit vereinfacht, die zwischen Run getan werden kann, und Wait)

+0

Ich denke, überprüfen task.Result, um festzustellen, ob ein Fehler aufgetreten ist oder nicht, ist auch keine gute Idee? Der Aufruf von task.Result löst hier eine Ausnahme aus ... und auch das Flag Fault ist immer falsch, auch wenn eine Ausnahme ausgelöst wurde. – user3292642

+0

@ user3292642 Ich glaube nicht, dass ich verstehe? Sie sollten auf die Eigenschaft 'Exception' zugreifen, um die Ausnahme zu verschlucken. – slawekwin

+0

Ich möchte die Ausnahme nicht verschlucken. Ich logge es bereits in der Continue-Methode ein. Nachdem die Protokollierung durchgeführt wurde, möchte ich zurückkehren, wenn eine Ausnahme ausgelöst wurde, aber ich weiß nicht wie. Die Überprüfung des Status ist auch keine Option, denke ich. – user3292642

2

Ich habe einen Aufruf an eine asynchrone Methode und möchte das Ergebnis abrufen.

The best way to do this is with await, not Result.

Kann jemand erklären warum?

Im ersten Beispiel deleteTask ist die von Task.Run zurück Aufgabe. Im zweiten Beispiel ist deleteTask die von ContinueWith zurückgegebene Aufgabe.Diese Aufgabe wird niemals ausgeführt, es sei denn, DeleteIndexAsync löst eine Ausnahme aus.

Und ist es möglich async/await anstelle von Task hier zu verwenden?

Ja, das ist der beste Ansatz. Wenn Sie mit asynchronem Code arbeiten, sollten Sie immer use await instead of ContinueWith. In diesem Fall bedeutet die TaskContinuationOptions.OnlyOnFaulted Sie die Fortsetzung Code in einem catch Block nach den await setzen sollen:

bool deleteResult; 
try 
{ 
    deleteResult = await Task.Run(() => DeleteIndexAsync(localItem)); 
} 
catch (Exception ex) 
{ 
    //handle and log exceptions 
} 
if (!deleteResult) 
    ... 

Oder, da es sieht aus wie DeleteIndexAsync asynchron ist, wäre Task.Run Entfernung geeignetere:

bool deleteResult; 
try 
{ 
    deleteResult = await DeleteIndexAsync(localItem); 
} 
catch (Exception ex) 
{ 
    //handle and log exceptions 
} 
if (!deleteResult) 
    ... 
Verwandte Themen