2016-09-23 132 views
0

我想了解一些(在我眼中)奇怪的行爲。我有一個調用一些異步方法,並希望檢索其結果。 (DeleteIndexAsync返回Task<bool>試圖瞭解Task.ContinueWith()

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

if (!deleteTask.Result) 

在這種情況下ResultfalseStatusWaitingForActivation

而這段代碼我想

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

    if (!deleteTask.Result) 

有人能解釋,爲什麼呢?是否有可能在這裏使用async/await而不是Task

編輯:

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

    if (errorOccurred) 
    { 
    return true; 
    } 

回答

2

如果鏈中的電話,像在第一個例子,你是分配給deleteTask變量的值實際上是第二Task。這是應該只在第一個任務失敗時運行的那個(調用DeleteIndexAsync的那個)。

這是因爲Task.RunTask.ContinueWith都返回Task它們創建。它解釋了爲什麼在第一個例子中你得到了Status == WaitingForActivation。在第一個片段中,訪問deleteTask.Result會導致拋出異常。在DeleteIndexAsync的情況下,這將是一個AggregateException包含原始異常(除非您訪問t.Exception),否則它會表明「操作已取消」 - 這是因爲您試圖獲得有條件地排定的任務的結果和條件沒有得到滿足。

如果由含有剪斷方法async你可以做這樣的(未測試):

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

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

關於質詢編輯:

使用捕獲變量將會有幫助,但當前解決方案引入競爭條件。在這種情況下,它會更好地這樣做:

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

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

,但在這一點上,你可以完全省略異步調用,因爲你馬上等待結果 - 除非這個例子簡化,可以Run之間完成工作和Wait

+0

我想檢查task.Result以確定是否發生錯誤不是一個好主意,以及?訪問task.Result在此處引發異常...並且Faulted標誌始終爲false,即使引發異常也是如此。 – user3292642

+0

@ user3292642我不覺得我明白嗎?你應該訪問'Exception'屬性來吞噬異常。 – slawekwin

+0

我不想吞下異常。我已經在continuewith方法中記錄它了。日誌記錄完成後,如果發生異常,我想返回,但我不知道如何。檢查狀態不是我想的一個選項。 – user3292642

2

我有一個調用一些異步方法,並希望檢索其結果。

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

有人可以解釋爲什麼嗎?

在第一個示例中,deleteTask是從Task.Run返回的任務。在第二個示例中,deleteTask是從ContinueWith返回的任務。除非DeleteIndexAsync引發異常,否則此任務不會執行。

是否有可能在這裏使用async/await而不是Task?

是的,這是最好的方法。使用異步代碼時,應始終使用use await instead of ContinueWith。在這種情況下,TaskContinuationOptions.OnlyOnFaulted意味着你應該把繼續碼在catchawait後:

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

或者,因爲它看起來像DeleteIndexAsync是異步的,除去Task.Run會更合適:

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