Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/sharepoint/4.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
.net 在异步方法的发布模式下未调用IDisposable.Dispose()_.net_Vb.net_Async Await - Fatal编程技术网

.net 在异步方法的发布模式下未调用IDisposable.Dispose()

.net 在异步方法的发布模式下未调用IDisposable.Dispose(),.net,vb.net,async-await,.net,Vb.net,Async Await,我使用VS2015.1上的.NET 4.6.1在VB.NET 14中编写了以下WPF示例应用程序: Class MainWindow Public Sub New() InitializeComponent() End Sub Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs) MessageBox.Show("Pre") Usi

我使用VS2015.1上的.NET 4.6.1在VB.NET 14中编写了以下WPF示例应用程序:

Class MainWindow

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
        MessageBox.Show("Pre")

        Using window = New DisposableWindow()
            window.Show()

            For index = 1 To 1
                Await Task.Delay(100)
            Next
        End Using

        MessageBox.Show("Post")
    End Sub

    Class DisposableWindow
        Inherits Window
        Implements IDisposable

        Public Sub Dispose() Implements IDisposable.Dispose
            Me.Close()
            MessageBox.Show("Disposed")
        End Sub
    End Class

End Class
下面的示例生成以下输出:

  • 调试模式:预调试、已处理、后调试
  • 发布模式:发布前、发布后
这很奇怪。为什么调试模式执行此代码与发布模式不同

当我将using块更改为手动try/finally块时,对window.Dispose()的调用甚至会引发NullReferenceException:

Dim window = New DisposableWindow()
Try
    window.Show()

    For index = 1 To 1
        Await Task.Delay(100)
    Next
Finally
    window.Dispose()
End Try
还有更奇怪的事情:当for循环被排除在外时,示例工作得非常完美。我只让For循环运行一次,以指定产生问题的循环的最小数量。也可以用While循环替换For循环。它产生与For循环相同的行为

作品:

Using window = New DisposableWindow()
    window.Show()

    Await Task.Delay(100)
End Using
现在你可能会想:‘这太奇怪了!’。情况变得更糟。 我在C#(6)中也给出了完全相同的例子,在这里它可以完美地工作。因此,在C#中,调试和发布模式都会将“Pre、Disposed、Post”作为输出

样本可在此处下载:

在这一点上我很困惑。这是.NET Framework的VB.NET堆栈中的错误吗?或者我正在尝试完成一些奇怪的事情,幸运的是,这些工作似乎是在C#中完成的,部分是在VB.NET中完成的

编辑:

做了更多的测试:

  • 在VB.NET的发布模式中禁用编译器优化,使其行为类似于调试模式(如预期,但希望测试它,以防万一)
  • 当我以.NET4.5(async/await最早的版本)为目标时,这个问题也会发生
更新:

这一问题已经解决。计划为1.2版发布公共版本,但主分支中的最新版本应包含修复程序


请看:

我来写这篇文章,这个Roslyn bug非常讨厌,很容易破坏很多VB.NET程序。以一种非常丑陋和难以诊断的方式

这个bug很难看到,您必须使用反编译器查看生成的程序集。我将以极快的速度描述它。Async子段中的语句被重写为状态机,代码段中的特定类名为VB$StateMachine\u 1\u按钮单击。你只能通过一个像样的反编译器来查看它。此类的
MoveNext()
方法执行方法体中的语句。在异步代码运行时多次输入此方法

需要捕获MoveNext()使用的变量,将局部变量转换为类的字段。与您的
窗口
变量一样,稍后当Using语句结束并且需要调用Dispose()方法时,将需要该变量。调试生成中此变量的名称为
$VB$ResumableLocal\u窗口$0
。当您构建程序的发布版本时,编译器会尝试优化该类,并且会出错。它消除了捕获并使
窗口成为MoveNext()的局部变量。这是非常错误的,当执行在
等待
之后恢复时,该变量将为空。因此它的Dispose()方法不会被调用

这个Roslyn bug对AICT有很大的影响,它会破坏任何在异步方法中使用
Using
语句的VB.NET代码,其中语句体包含一个wait。这是不容易诊断的,丢失的Dispose()调用常常无法被检测到。除了像你这样有明显副作用的情况。现在一定有很多在生产环境中运行的程序有这个bug。副作用是,它们将运行“繁重”,消耗超出必要的资源。程序可能以许多难以诊断的方式失败

对于这个bug有一个临时的解决方法,确保永远不要部署有其他问题的VB.NET应用程序的调试版本。而是关闭优化器。选择发布版本并使用项目>属性>编译选项卡>高级编译选项>取消选中“启用优化”复选框


Yekes,这很糟糕。

只是一种预感,但Dispose()调用似乎位于不同的线程上。它在调试中工作的原因是调试器正在为您切换线程。如果您添加了某种回调,从而将您切换回正确的线程,您可能会有更好的运气。例如,请参阅代码如何使用Invoke required:它看起来确实是一个bug。我能够在我的VS 2015中复制它。在任何情况下,它可能已经在上一个版本中修复(我没有上一个版本)。你应该下载上一个VS 2015如果错误仍然存在,你最好现在就让.NET团队(创建一个罚单),因为发布/调试中的不同行为看起来相当丑陋。还有一个可怕的Roslyn bug。这是一个非常,非常讨厌,很容易不诊断这一点。点击“新问题”按钮报告错误。问题创建:哇,听起来很难看。我真的很好奇,为什么只有等待语句处于for或while循环中时才会发生这种情况。我也相信Roslyn团队会喜欢你的分析!它很难看。For/While循环可能对这个bug很有帮助,但我没有深入分析它的副作用。也许可以解释为什么以前没有发现这个bug。重写异步子对象的Roslyn代码非常复杂,这些代码转换是编译器必须完成的最复杂的事情。