Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么Roslyn中有异步状态机类(而不是结构)? 让我们考虑这个非常简单的异步方法: static async Task myMethodAsync() { await Task.Delay(500); }_C#_.net_Language Lawyer_Roslyn - Fatal编程技术网

C# 为什么Roslyn中有异步状态机类(而不是结构)? 让我们考虑这个非常简单的异步方法: static async Task myMethodAsync() { await Task.Delay(500); }

C# 为什么Roslyn中有异步状态机类(而不是结构)? 让我们考虑这个非常简单的异步方法: static async Task myMethodAsync() { await Task.Delay(500); },c#,.net,language-lawyer,roslyn,C#,.net,Language Lawyer,Roslyn,当我用VS2013(前Roslyn编译器)编译时,生成的状态机是一个结构 private struct <myMethodAsync>d__0 : IAsyncStateMachine { ... void IAsyncStateMachine.MoveNext() { ... } } 私有结构d__0:IAsyncStateMachine { ... void IAsyncStateMachine.MoveNext() {

当我用VS2013(前Roslyn编译器)编译时,生成的状态机是一个结构

private struct <myMethodAsync>d__0 : IAsyncStateMachine
{  
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}
私有结构d__0:IAsyncStateMachine
{  
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
当我用VS2015(Roslyn)编译它时,生成的代码如下:

private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
    ...
    void IAsyncStateMachine.MoveNext()
    {
        ...
    }
}
专用密封类d__1:IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
正如您所见,Roslyn生成了一个类(而不是结构)。如果我没记错的话,在旧编译器(我猜是CTP2012)中,异步/等待支持的第一个实现也生成了类,然后出于性能原因将其更改为struct。(在某些情况下,您可以完全避免装箱和堆分配…(请参阅)

有人知道为什么罗斯林的情况又变了吗?(我对此没有任何问题,我知道这个更改是透明的,不会改变任何代码的行为,我只是好奇)

编辑:


不信者@Damien_(源代码:)的回答解释了一切。所描述的Roslyn行为仅适用于调试构建(由于注释中提到的CLR限制,这是必需的)。在发行版中,它还生成一个结构(具有该结构的所有优点)。因此,这似乎是一个非常聪明的解决方案,可以支持编辑和继续,并在生产中获得更好的性能。有趣的东西,谢谢大家的参与

我对这一点没有任何先见之明,但由于Roslyn现在是开源的,我们可以在代码中寻找解释

在这里,我们发现:

// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.
var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;

因此,虽然使用
struct
s有一定的吸引力,但允许在
async
方法中工作这一重大胜利显然被选为更好的选择。

很难给出这样的明确答案(除非编译器团队有人来访:),但您可以考虑以下几点:

结构的性能“奖金”总是一种折衷。基本上,您会得到以下结果:

  • 价值语义学
  • 可能的堆栈(甚至可能是寄存器?)分配
  • 避免间接
这在等待案件中意味着什么?嗯,实际上。。。没有什么。状态机在堆栈上的时间非常短-记住,
wait
有效地执行
返回,因此方法堆栈死亡;状态机必须保存在某个地方,而且“某个地方”肯定在堆上。堆栈生存期不适合异步代码:)

除此之外,状态机违反了定义结构的一些良好准则:

  • struct
    s应该最多16个字节大-状态机包含两个指针,它们自己在64位上整齐地填充了16个字节的限制。除此之外,还有状态本身,所以它超过了“极限”。这没什么大不了的,因为它很可能只是通过引用传递的,但是请注意,这不太适合structs的用例-一个基本上是引用类型的struct
  • struct
    s应该是不可变的-这可能不需要太多注释。这是一台状态机。同样,这也没什么大不了的,因为结构是自动生成的代码和私有的,但是
  • struct
    s应在逻辑上表示单个值。这里的情况肯定不是这样,但从一开始就有了一个可变的状态
  • 它不应该经常被装箱——这不是问题,因为我们到处都在使用泛型。状态最终位于堆的某个位置,但至少没有(自动)装箱。再说一次,它只在内部使用的事实使得它几乎是无效的
当然,所有这些都是在没有闭包的情况下发生的。当您有遍历
await
s的局部变量(或字段)时,状态会进一步膨胀,从而限制了使用结构的有效性

考虑到所有这些,类方法无疑是更干净的,我不希望使用
struct
来提高性能。所有涉及的对象都具有相似的生存期,因此提高内存性能的唯一方法是使所有对象都
struct
s(例如,存储在某个缓冲区中)——当然,这在一般情况下是不可能的。大多数情况下,您首先使用
wait
(即一些异步I/O工作)已经涉及到其他类,例如,数据缓冲区、字符串。。。您不太可能等待只返回
42
而不进行任何堆分配的东西


最后,我要说的是,你真正看到性能差异的地方只有基准测试。而优化基准测试是一个愚蠢的想法,至少可以说…

我怀疑他们认为复杂性(可重变结构)不值得这么做
async
方法几乎总是有一个真正的异步点—一个产生控制的
await
,这将要求对结构进行装箱。我相信structs只会减轻同步运行的
async
方法的内存压力。当你可以阅读源代码时,你并不总是需要编译器团队的成员,他们留下了一条有用的评论:-)@Damien_the_unsiver是的,这绝对是一个伟大的发现,我已经对你的答案投了赞成票:p如果代码不是异步运行的,结构会有很大帮助,例如数据已经在缓冲区中。非常好!基于此,我还发现:只有在调试中构建它时才会发生这种情况(有意义,就是在进行EnC…),但在发布时,它们会创建一个结构(显然是启用的)