IDisposable.Dispose()的发布模式为异步方法不叫 [英] IDisposable.Dispose() not called in Release mode for async method

查看:177
本文介绍了IDisposable.Dispose()的发布模式为异步方法不叫的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote the following WPF sample app in VB.NET 14 using .NET 4.6.1 on VS2015.1:

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

下面的示例产生下面的输出:

The sample below produces the following output:


  • 调试模式:pre,处置,邮政

  • Release模式:pre,邮政

这是奇怪的。为什么会调试模式不同执行此code比Release模式...?

This is strange. Why would Debug mode execute this code differently than Release mode...?

在我的使用块更改为手动try / finally块,调用window.Dispose()甚至抛出一个NullReferenceException:

When I change the using block to a manual try/finally block, the call to window.Dispose() even throws a 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循环运行一次,以指定产生的问题环路的最低金额。还随意更换for循环与while循环。它产生相同的行为for循环。

And even more strange stuff: When the for-loop is excluded, the sample works perfectly. I've only let the For-loop run once, to specify the minimum amount of loops the produce the issue. Also feel free to replace the For-loop with a While-loop. It produces the same behavior as the For-loop.

作品:

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

    Await Task.Delay(100)
End Using

现在你可能会想:这是奇怪!。它变得更糟。
我也用C#(6),它完美的作品做完全一样的例子。因此,在C#Debug和Release模式导致'pre,处置,邮政作为输出。

Now you might think: 'That is strange!'. It gets even worse. I've also made the exact same example in C# (6), where it works perfectly. So in C# both Debug and Release mode result in 'Pre, Disposed, Post' as output.

样品可以在这里下载:

http://www.filedropper.com/vbsample

http://www.filedropper.com/cssample

我在这一点难倒pretty。这是.NET Framework的VB.NET堆栈中的错误?还是我试图完成一些奇怪的事情,它通过运气似乎在C#和部分VB.NET中的工作?

I'm pretty stumped at this point. Is this a bug in the VB.NET stack of .NET Framework? Or am I trying to accomplish something strange, which by luck seems the work in C# and partially in VB.NET?

编辑:

做了一些更多的测试:


  • 禁用编译器优化在VB.NET的发布模式,使得它表现得像调试模式(如预期,但要测试它,以防万一)。

  • 问题还当我面向.NET 4.5(其中,异步/的await成为可用的最早版本)发生。

更新:

这已经被修复。公开发行计​​划于1.2版本,但在主分支的最新版本应该包含的修补程序。

This has since been fixed. Public release is planned for version 1.2, but the latest version in the master branch should contain the fix.

请参阅: https://github.com/dotnet/roslyn/issues/7669

推荐答案

我会写这一个,这罗斯林错误是极其肮脏和责任,打破了很多VB.NET程序。在一个非常丑陋和难以诊断的方法。

I'll write this one up, this Roslyn bug is exceedingly nasty and liable to break a lot of VB.NET programs. In a very ugly and difficult to diagnose way.

该错误是pretty很难看,你看用反编译生​​成的汇编。我会以极快的速度形容它。在异步子语句,可以改写成一个状态机,在你的代码片段的具体类名是VB $ StateMachine_1_buttonClick。您只能使用一个像样的反编译器看到它。这个类的的MoveNext()方法执行的方法体中的语句。这种方法多次输入,而你的异步code运行。

The bug is pretty hard to see, you have to look at the generated assembly with a decompiler. I'll describe it at break-neck speed. The statements in the Async Sub get rewritten into a state machine, the specific class name in your snippet is VB$StateMachine_1_buttonClick. You can only see it with a decent decompiler. The MoveNext() method of this class executes the statements in the method body. This method is entered multiple times while your async code runs.

由MoveNext的(使用的变量)必须的抓获的,把你的局部变量到类的字段。就像你的窗口变量,它会在以后需要的时候需要被调用的使用语句结束和Dispose()方法。在调试版本该变量的名称是 $ $ VB $ ResumableLocal_window 1,0 。当你建立你的程序的发布版本,编译器试图优化这个类和摸索严重。它的排除的捕捉,使窗口 MoveNext的局部变量()。这是可怕的错误,当执行等待,该变量将一无所获后恢复。因而其Dispose()方法将不会被调用。

Variables used by MoveNext() need to be captured, turning your local variables into fields of the class. Like your window variable, it will be needed later when the Using statement ends and the Dispose() method needs to be called. The name of this variable in the Debug build is $VB$ResumableLocal_window$0. When you build the Release build of your program, the compiler attempts to optimize this class and fumbles badly. It eliminates the capture and makes window a local variable of MoveNext(). This is horribly wrong, when execution resumes after the Await, that variable will be Nothing. And thus its Dispose() method won't be called.

这罗斯林缺陷有非常大的影响AFAICT,这将打破了使用使用声明一个异步方法的任何VB.NET code其中的声明体包含等待。这是不容易诊断,一个缺失的Dispose()调用非常往往在不知不觉中。除非像你这样的情况下它有一个非常明显的副作用。必须有生产批次运行的程序,有这个bug现在。副作用是,他们会遇到重,不是必要的消耗更多的资源。该程序可以在许多硬故障诊断方法。

This Roslyn bug has a very large impact afaict, it will break any VB.NET code that uses the Using statement in an Async method where the statement body contains an Await. This is not easy to diagnose, a missing Dispose() call very often goes undetected. Except in a case like yours where it has a very visible side-effect. There must be lots programs running in production that have this bug right now. Side-effect is that they'll run "heavy", consuming more resources than necessary. The program can fail in many hard to diagnose ways.

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

There is a temporary workaround for this bug, be sure to never deploy the Debug build of your VB.NET app, that has other problems. Turn off the optimizer instead. Select the Release build and use Project > Properties > Compile tab > Advanced Compile Options > untick the "Enable optimizations" checkbox.

哎呀,这是不好的。

这篇关于IDisposable.Dispose()的发布模式为异步方法不叫的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆