gpt4 book ai didi

c# - 将 Task.Run 与 async/await 关键字混合使用时出现奇怪的编译器行为

转载 作者:行者123 更新时间:2023-11-30 14:28:46 29 4
gpt4 key购买 nike

考虑以下 C# 代码

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 说:

Return Value is: A Task(TResult) that represents a proxy for the Task(TResult) returned by function.

它还在备注中说:

The Run<TResult>(Func<Task<TResult>>) method is used by language compilers to support the async and await keywords. It is not intended to be called directly from user code.


所以,看起来我不应该在我的代码中使用这个重载的 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.
是什么导致编译器将 L3 与 L1 和 L2 区别对待。为什么简单地添加 'async'到 L1(或从 L2 中删除 await)会导致编译器以不同方式对待它?


2.
如果将更多 Task.Run 级联到 L1/L2/L3,Visual Studio 会崩溃。我正在使用 VS2013,如果我级联 5 层或更多层的 Task.Run,​​它会崩溃。 4 层是我能得到的最好的,这就是我使用 4 层作为示例的原因。只有我吗 ?编译器在翻译 Task.Run 时发生了什么?

谢谢

最佳答案

What causes the Compiler to treat L3 differently from L1 and L2. Why simply adding 'async' to L1 (or removing await from L2) causes the compiler to treat it differently ?

因为 asyncawait更改 lambda 表达式中的类型。你可以想到async作为“添加” Task<> wrapper ,和 await作为“删除” 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>>>这次。等等。

If you cascade more Task.Run to L1/L2/L3, Visual Studio crashes. I am using VS2013, if I cascade 5 or more layers of Task.Run, it crashes. 4 layers is the best I can get, that's why I use 4 layers as my example. Is it just me ? What happen to the compiler in translating Task.Run ?

谁在乎?没有任何真实世界的代码会这样做。有一些众所周知的场景,编译器很难在合理的时间范围内处理; using lambda expressions in method overload resolution is one .使用 lambda 表达式的嵌套调用 makes the compiler work exponentially harder .

关于c# - 将 Task.Run 与 async/await 关键字混合使用时出现奇怪的编译器行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28357489/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com