[英]Task.Run( () MethodName()) and await Task.Run(async () => MethodName())
[英]Weird compiler behavior when mixing Task.Run with async / await keyword
var L1 =
Task.Run(() =>
{
return Task.Run(() =>
{
return Task.Run(() =>
{
return Task.Run(() =>
{
return new Dummy();
});
});
});
});
var L2 =
Task.Run(async () =>
{
return await Task.Run(async () =>
{
return await Task.Run(async () =>
{
return await Task.Run(async () =>
{
return new Dummy();
});
});
});
});
var L3 =
Task.Run(async () =>
{
return Task.Run(async () =>
{
return Task.Run(async () =>
{
return Task.Run(async () =>
{
return new Dummy();
});
});
});
});
var r1 = L1.Result;
var r2 = L2.Result;
var r3 = L3.Result;
================================================== ====================
乍一看,L1,L2和L3都看起来像
Task<Task<Task<Task<Dummy>>>>
事实证明,L1和L2只是Task<Dummy>
所以,我查找MSDN for Task.Run
(我重载的Task.Run是: Task.Run<TResult>(Func<Task<TResult>>)
)
MSDN说:
返回值是:一个Task(TResult),表示函数返回的Task(TResult)的代理。
它还在备注中说:
语言编译器使用
Run<TResult>(Func<Task<TResult>>)
方法来支持async和await关键字。 它无意直接从用户代码调用。
所以,看起来我不应该在我的代码中使用这个重载的Task.Run,
如果我这样做,编译器将剥离Task<Task<Task<...<TResult>>>>
的额外层,并简单地给你一个Task<TResult>
如果你鼠标悬停在L1和L2上,它会告诉你它是Task<Dummy>
,而不是我的
预期Task<Task<Task<Task<Dummy>>>>
到目前为止一直很好,直到我看到L3
L3看起来与L1和L2几乎完全相同。 不同之处是:
L3只有async
关键字,而不是await
,不像L1和L2,其中L1既没有它们也没有L2它们都有
令人惊讶的是,L3现在被视为Task<Task<Task<Task<Dummy>>>>
,与L1和L2不同,其中两者都被视为Task<Dummy>
我的问题:
1。
是什么导致编译器以不同于L1和L2的方式处理L3。 为什么简单地将'async'
添加到L1(或从L2中删除await
)会导致编译器对其进行不同的处理?
2。
如果您将更多Task.Run级联到L1 / L2 / L3,则Visual Studio会崩溃。 我正在使用VS2013,如果我级联5层或更多层的Task.Run,它会崩溃。 4层是我能得到的最好的,这就是为什么我使用4层作为我的例子。 只有我吗 ? 编译器在翻译Task.Run时会发生什么?
谢谢
是什么导致编译器以不同于L1和L2的方式处理L3。 为什么简单地将“async”添加到L1(或从L2中删除等待)会导致编译器对其进行不同的处理?
因为async
和await
更改了lambda表达式中的类型。 您可以将async
视为“添加” Task<>
包装器,并await
“删除” Task<>
包装器。
只需考虑最里面调用中涉及的类型。 首先,L1:
return Task.Run(() =>
{
return new Dummy();
});
() => { return new Dummy(); }
() => { return new Dummy(); }
是Func<Dummy>
等的返回类型的那超负荷Task.Run
是Task<Dummy>
。
因此() => ###Task<Dummy>###
是Func<Task<Dummy>>
,它调用Task.Run
的不同重载,返回类型为Task<Dummy>
。 等等。
现在考虑L2:
return await Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
async () => { return new Dummy(); }
是Func<Task<Dummy>>
,所以返回类型的那超负荷Task.Run
是Task<Dummy>
。
async () => await ###Task<Dummy>###
的类型async () => await ###Task<Dummy>###
是Func<Task<Dummy>>
,因此它调用Task.Run
的相同重载,结果类型为Task<Dummy>
。 等等。
最后,L3:
return Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
async () => { return new Dummy(); }
再次是Func<Task<Dummy>>
,所以返回类型的那超负荷Task.Run
是Task<Dummy>
。
async () => { return ###Task<Dummy>### }
是Func<Task<Task<Dummy>>>
。 请注意嵌套任务。 因此,再次调用Task.Run
的相同重载 ,但Task.Run
返回类型为Task<Task<Dummy>>
。
现在,您只需重复每个级别。 async () => { return ###Task<Task<Dummy>>### }
是Func<Task<Task<Task<Dummy>>>>
。 再次调用Task.Run
的相同重载 ,但这次它的返回类型是Task<Task<Task<Dummy>>>
Task.Run
。 等等。
如果您将更多Task.Run级联到L1 / L2 / L3,则Visual Studio会崩溃。 我正在使用VS2013,如果我级联5层或更多层的Task.Run,它会崩溃。 4层是我能得到的最好的,这就是为什么我使用4层作为我的例子。 只有我吗 ? 编译器在翻译Task.Run时会发生什么?
谁在乎? 没有现实世界的代码可以做到这一点。 众所周知的场景对于编译器来说在合理的时间范围内难以处理; 在方法重载解析中使用lambda表达式是其中之一 。 使用lambda表达式的嵌套调用使编译器的工作量指数级增加 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.