C# 我是否应该担心“此异步方法缺少 'await' 运算符并将同步运行”警告
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29923215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should I worry about "This async method lacks 'await' operators and will run synchronously" warning
提问by dannykay1710
I have a interface which exposes some async methods. More specifically it has methods defined which return either Task or Task<T>. I am using the async/await keywords.
我有一个公开一些异步方法的接口。更具体地说,它定义了返回 Task 或 Task<T> 的方法。我正在使用 async/await 关键字。
I am in the process of implementing this interface. However, in some of these methods this implementation doesn't have anything to await. For that reason I am getting the compiler warning "This async method lacks 'await' operators and will run synchronously..."
我正在实现这个接口。但是,在其中一些方法中,此实现无需等待。因此,我收到编译器警告“此异步方法缺少 'await' 运算符,将同步运行......”
I understand why I am getting the error but am wondering whether I should do anything about them in this context. It feels wrong to ignore compiler warnings.
我明白为什么我会收到错误,但我想知道在这种情况下我是否应该对它们做任何事情。忽略编译器警告感觉是错误的。
I know I can fix it by awaiting on a Task.Run but that feels wrong for a method that is only doing a few inexpensive operations. It also sounds like it will add unneeded overhead to the execution but then I am also not sure if that is already there because the async keyword is present.
我知道我可以通过等待 Task.Run 来修复它,但是对于只执行一些廉价操作的方法来说,这感觉是错误的。听起来它还会为执行增加不必要的开销,但我也不确定它是否已经存在,因为存在 async 关键字。
Should I just ignore the warnings or is there a way of working around this that I am not seeing?
我应该忽略警告还是有办法解决我没有看到的问题?
采纳答案by Michael Liu
The asynckeyword is merely an implementation detail of a method; it isn't part of the method signature. If one particular method implementation or override has nothing to await, then just omit the asynckeyword and return a completed task using Task.FromResult<TResult>:
的异步关键字仅仅是一个方法的实施方案的细节; 它不是方法签名的一部分。如果一个特定的方法实现或覆盖没有什么可等待的,那么只需省略async关键字并使用Task.FromResult<TResult>返回已完成的任务:
public Task<string> Foo() // public async Task<string> Foo()
{ // {
Baz(); // Baz();
return Task.FromResult("Hello"); // return "Hello";
} // }
If your method returns Taskinstead of Task<TResult>, then you can return a completed task of any type and value. Task.FromResult(0)seems to be a popular choice:
如果您的方法返回Task而不是Task<TResult>,那么您可以返回任何类型和值的已完成任务。Task.FromResult(0)似乎是一个流行的选择:
public Task Bar() // public async Task Bar()
{ // {
Baz(); // Baz();
return Task.FromResult(0); //
} // }
Or, as of .NET Framework 4.6, you can return Task.CompletedTask:
或者,从 .NET Framework 4.6 开始,您可以返回Task.CompletedTask:
public Task Bar() // public async Task Bar()
{ // {
Baz(); // Baz();
return Task.CompletedTask; //
} // }
回答by Ben Voigt
It's perfectly reasonable that some "asynchronous" operations complete synchronously, yet still conform to the asynchronous call model for the sake of polymorphism.
一些“异步”操作同步完成,但为了多态性仍然符合异步调用模型是完全合理的。
A real-world example of this is with the OS I/O APIs. Asynchronous and overlapped calls on some devices always complete inline (writing to a pipe implemented using shared memory, for example). But they implement the same interface as multi-part operations which do continue in the background.
这方面的一个真实示例是 OS I/O API。某些设备上的异步和重叠调用总是内联完成(例如,写入使用共享内存实现的管道)。但是它们实现了与在后台继续进行的多部分操作相同的接口。
回答by Vivian River
Michael Liu answered well your question about how you can avoid the warning: by returning Task.FromResult.
Michael Liu 很好地回答了您关于如何避免警告的问题:通过返回 Task.FromResult。
I'm going to answer the "Should I worry about the warning" part of your question.
我将回答您问题的“我是否应该担心警告”部分。
The answer is Yes!
答案是肯定的!
The reason for this is that the warning frequently results when you call a method that returns Taskinside of an async method without the awaitoperator. I just fixed a concurrency bug that happened because I invoked an operation in Entity Framework without awaiting the previous operation.
这样做的原因是,当您调用Task在没有await运算符的异步方法内部返回的方法时,经常会出现警告。我刚刚修复了一个并发错误,因为我在没有等待前一个操作的情况下调用了实体框架中的一个操作。
If you can meticulously write your code to avoid compiler warnings, then when there is a warning, it will stand out like a sore thumb. I could have avoided several hours of debugging.
如果你能一丝不苟地编写代码以避免编译器警告,那么当出现警告时,它会像拇指酸痛一样突出。我本可以避免几个小时的调试。
回答by Oleg Bondarenko
It might be too late but it might be useful investigation:
这可能为时已晚,但可能是有用的调查:
There is about inner structure of compiled code (IL):
关于编译代码(IL)的内部结构:
public static async Task<int> GetTestData()
{
return 12;
}
it becomes to in IL:
它在 IL 中变为:
.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1<int32>
GetTestData() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 28 55 73 61 67 65 4C 69 62 72 61 72 79 2E // ..(UsageLibrary.
53 74 61 72 74 54 79 70 65 2B 3C 47 65 74 54 65 // StartType+<GetTe
73 74 44 61 74 61 3E 64 5F 5F 31 00 00 ) // stData>d__1..
.custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 )
// Code size 52 (0x34)
.maxstack 2
.locals init ([0] class UsageLibrary.StartType/'<GetTestData>d__1' V_0,
[1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_1)
IL_0000: newobj instance void UsageLibrary.StartType/'<GetTestData>d__1'::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create()
IL_000c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_0011: ldloc.0
IL_0012: ldc.i4.m1
IL_0013: stfld int32 UsageLibrary.StartType/'<GetTestData>d__1'::'<>1__state'
IL_0018: ldloc.0
IL_0019: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_001e: stloc.1
IL_001f: ldloca.s V_1
IL_0021: ldloca.s V_0
IL_0023: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class UsageLibrary.StartType/'<GetTestData>d__1'>(!!0&)
IL_0028: ldloc.0
IL_0029: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder'
IL_002e: call instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task()
IL_0033: ret
} // end of method StartType::GetTestData
And without async and task method:
并且没有异步和任务方法:
public static int GetTestData()
{
return 12;
}
becomes :
变成:
.method private hidebysig static int32 GetTestData() cil managed
{
// Code size 8 (0x8)
.maxstack 1
.locals init ([0] int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.s 12
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // end of method StartType::GetTestData
As you could see the big difference between these methods. If you don't use await inside async method and do not care about using of async method (for example API call or event handler) the good idea will convert it to normal sync method (it saves your application performance).
正如您所看到的,这些方法之间的巨大差异。如果您不在 async 方法中使用 await 并且不关心使用 async 方法(例如 API 调用或事件处理程序),最好将其转换为普通的同步方法(它可以节省您的应用程序性能)。
Updated:
更新:
There is also additional information from microsoft docs https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth:
还有来自 microsoft docs https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth 的其他信息:
async methods need to have an await keyword in their body or they will never yield! This is important to keep in mind. If await is not used in the body of an async method, the C# compiler will generate a warning, but the code will compile and run as if it were a normal method. Note that this would also be incredibly inefficient, as the state machine generated by the C# compiler for the async method would not be accomplishing anything.
异步方法需要在它们的主体中有一个 await 关键字,否则它们将永远不会产生!记住这一点很重要。如果在异步方法的主体中未使用 await,则 C# 编译器将生成警告,但代码将像正常方法一样编译和运行。请注意,这也会非常低效,因为 C# 编译器为异步方法生成的状态机不会完成任何事情。
回答by tymtam
Note on exception behaviour when returning Task.FromResult
注意返回时的异常行为 Task.FromResult
Here's a little demo which shows the difference in exception handling between methods marked and not marked with async.
这是一个小演示,它显示了标记和未标记的方法之间的异常处理差异async。
public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!");
// Warning: This async method lacks 'await' operators and will run synchronously. Consider ...
public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!");
public string GetToken3Throws() => throw new Exception("Ex3!");
public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws);
public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");}
public static async Task Main(string[] args)
{
var p = new Program();
try { var task1 = p.GetToken1WithoutAsync(); }
catch( Exception ) { Console.WriteLine("Throws before await.");};
var task2 = p.GetToken2WithAsync(); // Does not throw;
try { var token2 = await task2; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
var task3 = p.GetToken3WithAsync(); // Does not throw;
try { var token3 = await task3; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
var task4 = p.GetToken4WithAsync(); // Does not throw;
try { var token4 = await task4; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
}
// .NETCoreApp,Version=v3.0
Throws before await.
Throws on await.
Throws on await.
Throws on await.
(Cross post of my answer for When async Task<T> required by interface, how to get return variable without compiler warning)
(我对当接口需要异步 Task<T> 时,如何在没有编译器警告的情况下获取返回变量的回答的交叉帖子)

