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

Sig*_*hol 3 c# asynchronous async-await

请考虑以下界面:

public interface IProvider
{
    Task<bool> Contains(string key);
}
Run Code Online (Sandbox Code Playgroud)

这是实现满足Visual Studio

public Task<bool> Contains(string key)
{
    return Task.FromResult(false);
}
Run Code Online (Sandbox Code Playgroud)

这种实现方便编写,似乎可以实现同样的目的:

public async Task<bool> Contains(string key)
{
    return false;
}
Run Code Online (Sandbox Code Playgroud)

然而,Visual Studio引发了一种混乱并坚持:

这种异步方法缺少"等待"运算符并将同步运行.考虑使用'await'运算符等待非阻塞API调用,或'await TaskEx.Run(...)'在后台线程上执行CPU绑定工作.

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

使用后一种选择会产生任何负面影响吗?

Mar*_*ell 13

其原因"嘘声像适合"是编译器需要做很多工作来呈现,在所有的预期正确的方式在这里工作的任务,你可以看到通过编译和反编译它

Task.FromResult更清洁,但可能仍然有开销 - IIRC有一些场景Task.FromResult可以在这里高效工作(每次返回相同的对象),但我不会依赖它.

有两种实用可靠的方法:

  • 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 ;
}
Run Code Online (Sandbox Code Playgroud)

要么

public ValueTask<bool> Contains(string key, string scope)
{
    return new ValueTask<bool>(false);
}
Run Code Online (Sandbox Code Playgroud)

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

生成的C#:

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

是这样的:

[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;
}
Run Code Online (Sandbox Code Playgroud)