试图理解Task.ContinueWith()

时间:2016-09-23 10:33:21

标签: c#

我试图了解一些(在我看来)奇怪的行为。我调用了一些异步方法,并希望检索其结果。 (DeleteIndexAsync返回Task<bool>

  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 个答案:

答案 0 :(得分: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;
}

但此时您可以完全删除异步调用,因为您会立即等待结果 - 除非此示例简化了RunWait之间可以完成的工作)

答案 1 :(得分: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表示您应该将延续代码放在catch之后的await块中:

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)
  ...