试图理解Task.ContinueWith()

本文关键字:ContinueWith Task | 更新日期: 2023-09-27 18:08:32

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

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

本场景中Resultfalse, StatusWaitingForActivation

而这段代码做了我想要的

  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;
  }

试图理解Task.ContinueWith()

如果您链接调用,如在第一个示例中,您分配给deleteTask变量的值实际上是第二个Task。它应该只在第一个任务(调用DeleteIndexAsync的任务)失败时运行。

这是因为Task.RunTask.ContinueWith都返回它们创建的Task s。它解释了为什么在第一个示例中得到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之间完成的工作)

我有一个异步方法的调用,想要检索它的结果。

最好的方法是使用await,而不是Result

有人能解释一下吗?

在第一个示例中,deleteTaskTask.Run返回的任务。在第二个示例中,deleteTask是从ContinueWith返回的任务。除非DeleteIndexAsync抛出异常,否则此任务永远不会实际执行。

是否可以在这里使用async/await而不是Task ?

是的,这是最好的方法。在处理异步代码时,应该始终使用await而不是ContinueWith。在本例中,TaskContinuationOptions.OnlyOnFaulted意味着您应该将延续代码放在await之后的catch块中:

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