[英]Are the await / async keywords needed under Task.Run?
I have an async lambda expression wrapped under Task.Run.我有一个包裹在 Task.Run 下的异步 lambda 表达式。 However, it looks like I can drop the async and await keywords and they will produce the same result.但是,看起来我可以删除 async 和 await 关键字,它们会产生相同的结果。
t1 = Task.Run(() => DoSomethingExpensiveAsync());
t2 = Task.Run(() => DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);
vs对比
var t1 = Task.Run(async () => await DoSomethingExpensiveAsync());
var t2 = Task.Run(async () => await DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);
There are actually three variants.实际上有三种变体。
var task = Task.Run(() => DoSomethingExpensiveAsync());
^ This one declares a new anonymous non-async function that calls DoSomethingExpensiveAsync()
and returns its Task
. ^ 这个函数声明了一个新的匿名非异步函数,它调用DoSomethingExpensiveAsync()
并返回它的Task
。 The compiler compiles this anonymous function and passes it as an argument to Task.Run()
.编译器编译这个匿名函数并将其作为参数传递给Task.Run()
。
var task = Task.Run( async () => await DoSomethingExpensiveAsync() );
^ This one declares a new anonymous async function that calls DoSomethingExpensiveAsync()
. ^ 这个函数声明了一个新的匿名异步函数,它调用DoSomethingExpensiveAsync()
。 It then returns an incomplete Task
, waits for DoSomethingExpensiveAsync()
to finish, and then signals the task as complete.然后它返回一个未完成的Task
,等待DoSomethingExpensiveAsync()
完成,然后发出任务完成的信号。
var task = Task.Run(DoSomethingExpensiveAsync);
^ This one does not declare a new anonymous function at all. ^ 这个根本没有声明一个新的匿名函数。 A direct reference to DoSomethingExpensiveAsync
will be passed as an argument to Task.Run()
.对DoSomethingExpensiveAsync
的直接引用将作为参数传递给Task.Run()
。
All of these are valid because all three versions return a Task
and therefore match the overload of Task.Run()
that accepts a Func<Task>
.所有这些都是有效的,因为所有三个版本都返回一个Task
并因此匹配接受Func<Task>
的Task.Run()
的重载。
As a black box, all three calls will end up doing the same thing.作为一个黑匣子,所有三个调用最终都会做同样的事情。 However the first two result in a new function being compiled (although I'm not certain it wouldn't be optimized away) and the second one also results in another state machine being created for it.然而,前两个导致编译一个新函数(尽管我不确定它不会被优化掉),而第二个也会导致为它创建另一个状态机。
The difference might be clearer if we rewrite them without using lambda expressions or anonymous functions.如果我们在不使用 lambda 表达式或匿名函数的情况下重写它们,差异可能会更清楚。 The following code does exactly the same thing:以下代码完全相同:
//This is the same as Task.Run( () => DoSomethingExpensiveAsync());
Task Foo()
{
return DoSomethingExpensiveAsync();
}
var task = Task.Run(Foo);
//This is the same as Task.Run(async () => await DoSomethingExpensiveAsync());
async Task Bar()
{
return await DoSomethingExpensiveAsync();
}
var task = Task.Run(Bar);
The difference between these two is that one "elides" tasks while the other doesn't.这两者之间的区别在于,一个“省略”了任务,而另一个则没有。 Stephen Cleary has written awhole blog on the subject . Stephen Cleary 写了一篇关于这个主题的完整博客。
How come the compiler let me do this and what is happening behind the scene?为什么编译器让我这样做以及幕后发生了什么?
The overload of Task.Run
that you're invoking takes a Func<Task>
- that is, a Task
-returning function.您正在调用的Task.Run
的重载需要一个Func<Task>
- 即一个Task
返回函数。 It doesn't matter where the Task
comes from; Task
来自哪里并不重要; the function just needs to return it from somewhere.该函数只需要从某个地方返回它。
If you pass a delegate without async
and await
, then the delegate is just calling a Task
-returning function and returns that same Task
.如果您在没有async
和await
情况下传递委托,则该委托只是调用Task
返回函数并返回相同的Task
。 If you pass a delegate with async
and await
, then the delegate calls the Task
-returning function and await
s it;如果您使用async
和await
传递委托,则委托会调用Task
返回函数并await
它; the actual Task
returned from the delegate is created by the async
keyword .从委托返回的实际Task
是由async
关键字创建的。
In this case, the two are semantically equivalent.在这种情况下,两者在语义上是等价的。 Using the async
/ await
keywords are a bit less efficient, since the compiler creates a state machine for the async
delegate.使用async
/ await
关键字效率稍低,因为编译器为async
委托创建了一个状态机。
Is there a situation where adding them will make a difference?是否存在添加它们会产生影响的情况?
Yes . 是的。 In the general case, you should keep async
and await
.在一般情况下,您应该保持async
和await
。 Only remove them in extremely simple "passthrough" situations like the one here.只有在非常简单的“直通”情况下才能删除它们,比如这里的情况。
Your code is the same as你的代码是一样的
t1 = DoSomethingExpensiveAsync();
t2 = DoSomethingExpensiveAsync();
await Task.WhenAll( t1, t2 );
because Task.Run( Func< function ) will return a proxy of the task generated by function.因为Task.Run( Func< function )将返回由函数生成的任务的代理。 There is no other Task created, and so you are awaiting the original tasks.没有创建其他任务,因此您正在等待原始任务。
When you already have an async method then there is no need to use Task.Run
at all.如果您已经有了异步方法,则根本不需要使用Task.Run
。
Without knowing what DoSomethingExpensiveAsync
it's impossible to tell for certain what will happen.如果不知道DoSomethingExpensiveAsync
是什么,就无法确定会发生什么。
Let's assume DoSomethingExpensiveAsync
is something like this:让我们假设DoSomethingExpensiveAsync
是这样的:
async Task DoSomethingExpensiveAsync()
{
SynchronousMethod();
await AsynchronousMethod();
}
In the first snippet, Task.Run
will schedule the invocation of DoSomethingExpensiveAsync
to the thread pool and returns as soon as SynchronousMethod
returns.在第一个片段中, Task.Run
会将DoSomethingExpensiveAsync
的调用调度到线程池,并在SynchronousMethod
返回后立即返回。
In the second snippet, Task.Run
will schedule the invocation of DoSomethingExpensiveAsync
to the thread pool and returns when the Task
return by DoSomethingExpensiveAsync
is completed.在第二个代码段中, Task.Run
会将DoSomethingExpensiveAsync
的调用调度到线程池,并在DoSomethingExpensiveAsync
的Task
返回完成时返回。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.