【问题标题】:Async method with no await vs Task.FromResult没有等待的异步方法与 Task.FromResult
【发布时间】:2018-10-03 16:00:14
【问题描述】:

考虑如下界面:

public interface IProvider
{
    Task<bool> Contains(string key);
}

这是满足 Visual Studio 的实现

public Task<bool> Contains(string key)
{
    return Task.FromResult(false);
}

这个实现写起来很方便,而且似乎可以达到同样的效果:

public async Task<bool> Contains(string key)
{
    return false;
}

然而,Visual Studio 却不以为然,坚持认为:

此异步方法缺少“等待”运算符,将同步运行。考虑使用 'await' 运算符来等待非阻塞 API 调用,或使用 'await TaskEx.Run(...)' 在后台线程上执行 CPU 密集型工作。

我很乐意忽略该警告并避免使用Task.FromResult(...)

使用后一个选项有什么负面影响吗?

【问题讨论】:

  • 为什么?使用Task.FromResult() 是正确的调用,因为您的代码 异步执行任何操作。 async 不会让某些东西异步运行,它只是语法糖,允许您 await 已经异步操作。使用async 只是为了返回false 并不方便,它比简单地返回Task&lt;bool&gt; 实例详细。
  • 顺便说一句,这段代码有什么意义?如果您不打算异步运行任何东西,为什么要使用任务?为什么不写一个public bool Contains(..)
  • 您的第一个实现只返回一个Task&lt;TResult&gt; and does noting asynchronous.。您的第二个实现标有async,并且您没有执行任何异步运行任务。当然,您会收到警告。顺便说一句:您的界面和方法的参数数量不同。你确定你实现了这个接口吗?
  • @PanagiotisKanavos:我认为这是Task.FromResult 的用例。当接口允许异步调用者,但实现是同步的。
  • @PanagiotisKanavos - 如果其他人定义了 IProvider 并且您正在尝试实现它,您将没有太多选择。

标签: c# asynchronous async-await


【解决方案1】:

“嘶嘶作响”的原因是编译器需要做大量的工作才能在此处呈现以所有预期正确方式工作的任务,您可以看到 by compiling and decompiling it

Task.FromResult 更干净,但可能仍有开销 - IIRC 在某些情况下Task.FromResult 可能在这里有效工作(每次返回相同的对象),但我不会依赖它。

有两种务实可靠的方法:

  • 每次都返回一个重复使用的静态 Task&lt;bool&gt; 结果
  • 使用ValueTask&lt;bool&gt; - 如果您经常同步返回,这似乎很理想

private readonly static Task<bool> s_False = Task.FromResult(false);
public Task<bool> Contains(string key, string scope)
{
    return s_False ;
}

public ValueTask<bool> Contains(string key, string scope)
{
    return new ValueTask<bool>(false);
}

注意:在这种情况下,第二个可能是不可能的,因为你没有定义接口。但是:如果您曾经正在设计一个需要允许异步使用但实际上可能是同步的接口:考虑使用ValueTask&lt;T&gt; 作为交换类型,而不是Task&lt;T&gt;

生成的C#:

public async System.Threading.Tasks.Task<bool> Contains(string key, string scope)
{
    return false;
}

类似于:

[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Contains>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<bool> <>t__builder;

    private void MoveNext()
    {
        bool result;
        try
        {
            result = false;
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <>t__builder.SetResult(result);
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
        <>t__builder.SetStateMachine(stateMachine);
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

[AsyncStateMachine(typeof(<Contains>d__0))]
public Task<bool> Contains(string key, string scope)
{
    <Contains>d__0 stateMachine = default(<Contains>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

【讨论】:

  • 感谢您的回答,尤其是对一段看似简单的代码幕后发生的事情的洞察。有时很容易忘记 CLR 的深处到底发生了多少事情。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多