简体   繁体   English

Task.Run 下是否需要 await / async 关键字?

[英]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);
  1. How come the compiler let me do this and what is happening behind the scene?为什么编译器让我这样做以及幕后发生了什么?
  2. Is there a situation where adding them will make a difference?是否存在添加它们会产生影响的情况?

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 .如果您在没有asyncawait情况下传递委托,则该委托只是调用Task返回函数并返回相同的Task If you pass a delegate with async and await , then the delegate calls the Task -returning function and await s it;如果您使用asyncawait传递委托,则委托会调用Task返回函数并await它; the actual Task returned from the delegate is created by the async keyword .从委托返回的实际Taskasync关键字创建的

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 .在一般情况下,您应该保持asyncawait 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的调用调度到线程池,并在DoSomethingExpensiveAsyncTask返回完成时返回。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM