从TaskCompletionSource </tresult>返回Task而不是Task <tresult>

时间:2013-04-26 06:13:53

标签: c# .net task-parallel-library async-await

正如我在几个coding examples中看到的那样,以及我从这个SO question可以理解的东西,我应该能够从TaskCompletionSource返回一个非泛型的任务

(i.e., Return Task and not Task<TResult> from the method UploadFilesAsync) 

以下代码:

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
   var tcs = new TaskCompletionSource<Object>();

   //logic to process files

   try
   {
      await Task.WhenAll(uploadFileAAsync(fileAPath), 
                         uploadFileBAsync(fileBPath));
      tcs.TrySetResult(null);
   }
   catch (Exception e)
   {
      tcs.SetException(e);
   }
   finally
   {
      //logic to clean up files
   }
   return tcs.Task;
}

产生以下语法错误

'UploadFilesAsync(string, string)' is an async method that returns 'Task', 
a return keyword must not be followed by an object expression. 
Did you intend to return 'Task<T>'?

我的目标是.NET 4.5。我知道它可以返回Task(对象),但这会使API感觉“脏”。是首选返回Task(对象)还是可以返回Task(非泛型,如代码所示)?

2 个答案:

答案 0 :(得分:25)

  

我知道它可以用来返回Task(对象)

嗯,这不符合你的预期。

问题在于您尝试返回任务...而异步方法会自动将返回值包装在另一个任务中。说实话,目前还不清楚为什么你在这里使用异步方法。为什么不写这个:

public Task UploadFilesAsync(string fileAPath, string fileBPath)
{
    return Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}

这不符合你的要求吗?你只想要一个在两个“子操作”完成时完成的任务,对吧?这正是Task.WhenAll返回的内容。您的方法仍然是非阻塞的 - 它不会等到操作完成后再返回。只是你正在使用Task.WhenAll无阻塞的事实来实现它,而不是异步方法。

编辑:请注意,如果您想在该方法中执行其他操作,则可以在不使用TaskCompletionSource的情况下将其设为异步方法:

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
    // Upload the files
    await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
    await SomethingElseAsync();
    MaybeDoSomethingCheap();
}

请注意,即使此处的异步方法返回Task,您也没有返回值 - async / await机器会为您处理所有这些,返回一个将在{{1}时完成的任务如果抛出异常,则已完成或出错。

答案 1 :(得分:2)

据我所知,使用Task时无法直接返回TaskCompletionSource<T>个对象。

通常我更喜欢在这些情况下返回Task<bool>类型的对象。但你是对的,如果函数的返回值不可用,则返回泛型类型对象是没有意义的。

但实际上您不需要创建TaskCompletionSource,因为函数内部有await个关键字。 TaskCompletionSource通常用于将同步函数转换为异步函数。由于您已经在调用异步函数(实际上这似乎是唯一的功能),因此您无需创建TaskCompletionSource

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
        return await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}