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 Task.Run
(我的重载Task.Run是:Task.Run<TResult>(Func<Task<TResult>>)
)
MSDN说:
返回值是:一个任务(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时会发生什么?
由于
答案 0 :(得分:6)
是什么导致编译器以不同于L1和L2的方式处理L3。为什么简单地添加&#39; async&#39;到L1(或从L2中删除await)导致编译器以不同的方式处理它?</ p>
因为async
和await
会更改lambda表达式中的类型。您可以将async
视为&#34;添加&#34;一个Task<>
包装,await
为&#34;删除&#34;一个Task<>
包装器。
只考虑最里面调用中涉及的类型。首先,L1:
return Task.Run(() =>
{
return new Dummy();
});
() => { return new Dummy(); }
的类型为Func<Dummy>
,因此that overload of Task.Run
的返回类型为Task<Dummy>
。
因此() => ###Task<Dummy>###
的类型为Func<Task<Dummy>>
,其调用different overload of Task.Run
,返回类型为Task<Dummy>
。等等。
现在考虑L2:
return await Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
的类型为Func<Task<Dummy>>
,因此that overload of Task.Run
的返回类型为Task<Dummy>
。
async () => await ###Task<Dummy>###
的类型为Func<Task<Dummy>>
,因此它会调用same overload of Task.Run
,其结果类型为Task<Dummy>
。等等。
最后,L3:
return Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
的类型再次为Func<Task<Dummy>>
,因此that overload of Task.Run
的返回类型为Task<Dummy>
。
async () => { return ###Task<Dummy>### }
的类型为Func<Task<Task<Dummy>>>
。请注意嵌套任务。因此,再次调用same overload of Task.Run
,但此次返回类型为Task<Task<Dummy>>
。
现在,您只需重复每个级别。 async () => { return ###Task<Task<Dummy>>### }
的类型为Func<Task<Task<Task<Dummy>>>>
。再次调用same overload of Task.Run
,但此次返回类型为Task<Task<Task<Dummy>>>
。等等。
如果将更多Task.Run级联到L1 / L2 / L3,Visual Studio会崩溃。我正在使用VS2013,如果我级联5层或更多层的Task.Run,它会崩溃。 4层是我能得到的最好的,这就是我使用4层作为例子的原因。只有我吗 ?翻译Task.Run时编译器会发生什么?
谁在乎?没有现实世界的代码可以做到这一点。众所周知的场景对于编译器来说在合理的时间范围内难以处理; using lambda expressions in method overload resolution is one。使用lambda表达式嵌套调用makes the compiler work exponentially harder。