Task.FromResult() 与 Task.Run()

Task.FromResult() vs. Task.Run()

我最近遇到过很多情况,其中 async 方法同步执行,但 return 无论如何都是一个任务,因此可以等待它们,例如

public virtual Task CreateAsync(TUser user)
{
    ThrowIfDisposed();
    if (user == null) throw new ArgumentNullException("user");
    Context.Save(user);
    Context.Flush();
    return Task.FromResult(0);
}

当然,最好将可能较长的 运行 操作分派给一个线程和 return 仍然活跃的任务,真正等待:

public virtual Task CreateAsync(TUser user)
{
    ThrowIfDisposed();
    if (user == null) throw new ArgumentNullException("user");
    return Task.Run(() =>
    {
        Context.Save(user);
        Context.Flush();
    });
}

不过,我有些怀疑,仅仅分离 TPL 线程并不是最安全的做法。对这两种不同的模式有什么评论吗?

如果您的方法是同步的,您不应该 return 一个 Task 开始。只需创建一个传统的同步方法即可。

如果出于某种原因这是不可能的(例如,您实现了一些异步接口)return使用 Task.FromResult 完成任务,在这种情况下甚至更好 Task.CompletedTask(已添加在 .NET 4.6 中)比在实现中使用 Task.Run 要好得多:

public virtual Task CreateAsync(TUser user)
{
    // ...
    return Task.CompletedTask;
}

如果您的 API 的消费者非常关心 Task-returning 方法而不是同步 运行 他们可以使用 Task.Run 自己来制作当然。

您应该记住,异步方法可能有相当大的同步部分(第一个 await 之前的部分),即使它们最终确实以异步方式继续。 无论如何,您不能立即采用异步方法 return a Task

Task.FromResult 实际上并没有创建或 运行 一个任务,它只是将返回的结果包装在一个任务对象中。我个人在 Unit Tests 中使用它,我需要模拟 Async 方法,当然我不想在单元测试中 运行 实际任务。

此外 Task.Run 实际上会在 TaskScheduler 上创建一个任务和 运行 一个任务。不建议在进行 Async 编程时使用 Task.Run。而是在任务上使用 await。参见 Stephen Cleary 的一些 do's and don't of Tasks