【问题标题】:Return Task instead of Task<TResult> from TaskCompletionSource从 TaskCompletionSource 返回 Task 而不是 Task<TResult>
【发布时间】:2013-04-26 06:13:52
【问题描述】:

正如我在几个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(of object) 但这会使 API 感觉“脏”。是返回 Task(of object) 的首选做法,还是可以返回 Task (代码中显示的非泛型)?

【问题讨论】:

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


    【解决方案1】:

    我知道它可以返回任务(对象)

    嗯,这不会像你期望的那样。

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

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

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

    【讨论】:

    • 我必须解压缩文件,运行上传,然后删除解压缩的文件。我希望上传运行异步,这就是为什么我将它们包装在 taskcompletionsource 中......或者我会按照你的建议做。这是有道理的,异步将返回包装在另一个任务中(难怪很难看到一个使用 async/await 的工作示例)。谢谢!
    • @JonathanHarrison:只是检查一下 - 你明白为什么你可以按照我的建议去做,而且它仍然是异步的吗?我再补充一点可能会有所帮助...
    • 非常感谢您进行编辑。这个概念最初并没有被点击......这很漂亮,async/await 实际上可以处理这样的 async void 方法。
    • @JonathanHarrison:是的——基本上你应该尽可能避免编写真正的async void Foo 方法;他们应该几乎总是返回Task。我很高兴它现在被点击了:)
    【解决方案2】:

    据我所知,在使用 TaskCompletionSource&lt;T&gt; 时,没有直接的方法可以返回 Task 对象。

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

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

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

    【讨论】:

    • 感谢您的回复,您所写的内容很有意义。请参阅我对 Jon Skeet 的评论,了解我为什么使用 TaskCompletionSource。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-05
    • 2014-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    相关资源
    最近更新 更多