2016-09-23 137 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) 
    ...