没有等待的异步方法与Task.FromResult

时间:2018-09-03 08:48:20

标签: c# asynchronous async-await

考虑以下界面:

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(...)

使用后一种选择是否有负面影响?

1 个答案:

答案 0 :(得分:12)

之所以选择“杂乱无章”,是因为编译器需要做很多工作才能在这里展示可以预期的正确方式工作的任务,您可以看到{{3 }}

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

有2种实用的可靠方法:

  • 每次返回可重复使用的静态Task<bool>结果
  • 使用ValueTask<bool>-如果您经常同步返回,在这里看起来很理想

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<T>作为交换类型,而不是Task<T>

生成的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;
}