C# Task.FromResult()与Task.Run()的比较

C# Task.FromResult()与Task.Run()的比较,c#,multithreading,asynchronous,async-await,task-parallel-library,C#,Multithreading,Asynchronous,Async Await,Task Parallel Library,我最近遇到过很多情况,async方法是同步执行的,但无论如何都会返回一个任务,因此可以等待它们,例如 public virtual Task CreateAsync(TUser user) { ThrowIfDisposed(); if (user == null) throw new ArgumentNullException("user"); Context.Save(user); Context.Flush(); return Task.FromRe

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

public virtual Task CreateAsync(TUser user)
{
    ThrowIfDisposed();
    if (user == null) throw new ArgumentNullException("user");
    Context.Save(user);
    Context.Flush();
    return Task.FromResult(0);
}
当然,最好将可能长时间运行的操作分派给线程,并返回仍然处于活动状态的任务,以便真正等待:

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

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

如果您的方法是同步的,您不应该首先返回
任务。只需创建一个传统的同步方法

如果由于某些原因而无法返回已完成的任务(例如,您实现了一些异步接口),则使用
task.FromResult
返回已完成的任务,或者在本例中更好地使用
task.CompletedTask
(添加在.NET 4.6中)比在实现中使用
task.Run
要好得多:

public virtual Task CreateAsync(TUser user)
{
    // ...
    return Task.CompletedTask;
}
如果API的使用者非常关心未同步运行的
任务
-返回方法,他们可以使用
任务。自己运行
来确保


您应该记住,异步方法可能有相当多的同步部分(第一次等待之前的部分),即使它们最终以异步方式继续无论如何,您不能假设异步方法立即返回任务。

任务。FromResult
实际上并不创建或运行任务,而是将返回的结果包装到任务对象中。我个人在单元测试中使用了它,我需要模拟异步方法,当然我不想在单元测试中运行实际任务


此外,
Task.Run
实际上会创建一个任务并在TaskScheduler上运行一个任务。不建议使用
任务。在执行
异步编程时运行
。在任务中使用
wait
。请参阅Stephen Cleary的《少数》。

如果可能,您应该调用实际的异步方法,而不是其中任何一种方法。@SLaks第二种方法以什么方式不异步(除了不使用新关键字)?(来自不经常使用异步功能的人)@KyleW:它仍然浪费一个线程。看@SLaks好吧,我明白你的意思。最好等待上下文。保存(用户)
并从那里开始。如果我们假设Context.Save是第三方,并且我们不能在那里添加异步,那么我们的选项是什么?似乎在OP中给出的选项中,第一个阻塞当前线程的选项要比第二个差得多,第二个至少将其分流到另一个线程并允许当前继续?