IDisposable 对象上的 using 语句 - 调用 Dispose 方法的延迟

发布于 2024-09-29 18:06:44 字数 528 浏览 8 评论 0原文

正如这篇文章所描述的,关于在 IDisposable 对象上使用 using 的用法,它说了一句有趣的话:

...使用块,Dispose 方法会在块结束后的某个时间自动调用。 (它可能不是立即的;它取决于 CLR。​​)

这里有趣的是“它可能不是立即的;它取决于 CLR”。 谁能提供更多详细信息吗?因为我们遇到了一些奇怪的情况,似乎在代码中 using(new MyDisposable()) {...}, after end of block } 它立即调用 MyDisposable 实例上的 Dispose 方法,但有些时间稍后。

更新:对我来说,结论是,在我看来,我在其他地方遇到了问题。我认为 Dispose 方法可以在 using 块结束后一段时间后调用。但如果不是这样,我必须在代码中的其他地方找到问题。 感谢您的回复!

As describe this article, about usage of using on IDisposable objects, it says one interesting words:

...using block, the Dispose method is automatically called sometime after the block ends. (It may not be immediate; it depends on the CLR.)

Interesting here is "It may not be immediate; it depends on the CLR".
Can anyone provide more details on this? Because we have some strange situations where it seems that on code using(new MyDisposable()) {...}, after end of block } it does NOT immediately calls Dispose method on MyDisposable instance, but some time later.

UPDATE: Conclusion for me, it seems to me that i have problem elsewhere. I think that Dispose method can be called some time later after using block ends. But when it is not like that, than i must find problem somewhere else in my code.
Thanks for responses!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

柒七 2024-10-06 18:06:44
using (SomeDisposableResource resource = new SomeDisposableResource())
{
    // TODO: use the resource
}

相当于:

SomeDisposableResource resource = new SomeDisposableResource();
try
{
    // TODO: use the resource
}
finally
{
    if (resource != null)
    {
        ((IDisposable)resource).Dispose();
    }
}

所以,由你来下结论。一切都取决于您如何定义“立即”。在多线程环境中,可以在 try 块和资源处置之间执行其他操作,但由于它包装在 finally 块中,因此可以保证 Dispose 方法将被调用。

using (SomeDisposableResource resource = new SomeDisposableResource())
{
    // TODO: use the resource
}

is equivalent to:

SomeDisposableResource resource = new SomeDisposableResource();
try
{
    // TODO: use the resource
}
finally
{
    if (resource != null)
    {
        ((IDisposable)resource).Dispose();
    }
}

so, up to you to draw conclusions. Everything depends on how you define immediate. In a multithreaded environment other actions could be performed between the try block and the disposal of the resource but as it is wrapped in a finally block it is guaranteed that the Dispose method will be called.

忱杏 2024-10-06 18:06:44

我对这个说法有点怀疑,并认为他们还有别的意思(也许是垃圾收集)。 using 语句只是 try/finally 块的语法糖,其中 finally 块调用 dispose。给定这个 C#:

using (var fs = new FileStream("C:\\blah.txt", FileMode.CreateNew))
{
    fs.WriteByte(7);
}

IL 看起来像这样:

//snipped
L_000e: nop 
L_000f: ldstr "C:\\blah.txt"
L_0014: ldc.i4.1 
L_0015: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode)
L_001a: stloc.0 
L_001b: nop 
L_001c: ldloc.0 
L_001d: ldc.i4.7 
L_001e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8)
L_0023: nop 
L_0024: nop 
L_0025: leave.s L_0037
L_0027: ldloc.0 
L_0028: ldnull 
L_0029: ceq 
L_002b: stloc.1 
L_002c: ldloc.1 
L_002d: brtrue.s L_0036
L_002f: ldloc.0 
L_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0035: nop 
L_0036: endfinally 
L_0037: nop 
L_0038: nop 
L_0039: ret 
.try L_001b to L_0027 finally handler L_0027 to L_0037

注意最后一行它只是一个 .try 和 .finally。 C# 规范中的 using 语句

I'm a little skeptical of that statement, and think they meant something else (perhaps garbage collection). A using statement is just syntactic sugar for a try/finally block where the finally block calls dispose. Given this C#:

using (var fs = new FileStream("C:\\blah.txt", FileMode.CreateNew))
{
    fs.WriteByte(7);
}

The IL looks like this:

//snipped
L_000e: nop 
L_000f: ldstr "C:\\blah.txt"
L_0014: ldc.i4.1 
L_0015: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode)
L_001a: stloc.0 
L_001b: nop 
L_001c: ldloc.0 
L_001d: ldc.i4.7 
L_001e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8)
L_0023: nop 
L_0024: nop 
L_0025: leave.s L_0037
L_0027: ldloc.0 
L_0028: ldnull 
L_0029: ceq 
L_002b: stloc.1 
L_002c: ldloc.1 
L_002d: brtrue.s L_0036
L_002f: ldloc.0 
L_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0035: nop 
L_0036: endfinally 
L_0037: nop 
L_0038: nop 
L_0039: ret 
.try L_001b to L_0027 finally handler L_0027 to L_0037

Notice on the last line it's just a .try and .finally. This is also indicated in The using statement from the C# spec.

半衬遮猫 2024-10-06 18:06:44

奇怪……非常奇怪……

这篇文章可能是错误的。 using 语句被完全编译,因为

MyDisposableObject obj = new MyDisposableObject()
try
{
    obj.Use();
}
finally
{
    if (obj!=null) obj.Dispose();
}

Dispose 方法是在 finally 块中显式调用的,与之前调用的析构函数/Finalize 方法不同收集,但由 GC 自行决定。

我认为这是文章中的一个错误。最多...某个时候可能指的是线程调度。如果 CLR 决定在到达finally后调度其他线程,那么您可能会在 100% 加载的 CPU 和正在运行的更高优先级任务上等待一些非常时间来运行 Dispose。

Dispose 同步重要!!!考虑一下这个有关托管资源的示例

public void Log(string message)
{
    using(StreamWriter sw = new StreamWriter(File.Append(path)))
    {
        sw.WriteLine(message);
    }
}
public static void Main()
{
    Log("Hello");
    Log("World");
}

对流和文件的 Dispose 调用实际上会关闭它们。如果文章中所写的内容属实,您将使用打开文件调用第二个Log,从而立即导致IOException

Strange... very strange...

The article is probably wrong. The using statement is compiled exactly as

MyDisposableObject obj = new MyDisposableObject()
try
{
    obj.Use();
}
finally
{
    if (obj!=null) obj.Dispose();
}

The Dispose method is explicitly called in the finally block, unlike the destructor/Finalize method which is called prior to collection but at GC's discretion.

I think it's an error in the article. At most... that sometime may refer to thread scheduling. If CLR decides to schedule other threads once hit the finally, then you might wait up some very little time on 100% loaded CPU and higher-priority tasks running to run Dispose.

It's important for Dispose to be synchronous!!! Think about this example about managed resources

public void Log(string message)
{
    using(StreamWriter sw = new StreamWriter(File.Append(path)))
    {
        sw.WriteLine(message);
    }
}
public static void Main()
{
    Log("Hello");
    Log("World");
}

The Dispose call, on streams and files, actually closes them. If what's written in the article was ever true, you would be calling the second Log with an open file, thus causing IOException immediately!

花之痕靓丽 2024-10-06 18:06:44

我不知道它来自哪里,这与我所看到的有关 using 语句的所有其他内容相矛盾,例如 这篇文章 说像这样的 using

using (Font font1 = new Font("Arial", 10.0f)) {
  byte charset = font1.GdiCharSet;
}

是这样实现的:

Font font1 = new Font("Arial", 10.0f);
try {
    byte charset = font1.GdiCharSet;
} finally {
  if (font1 != null) {
    ((IDisposable)font1).Dispose();
  }
}

所以,Dispose 方法将在 using 块后面的下一条语句之前调用。

写这篇文章的人可能会混淆一个对象在不再使用一段时间后如何被垃圾收集,或者未处理的一次性对象如何由后台线程调用其终结器。

I don't know where that comes from, and that contradicts everything else I have seen about the using statement, for example this article that says that a using block like this:

using (Font font1 = new Font("Arial", 10.0f)) {
  byte charset = font1.GdiCharSet;
}

is implemented like this:

Font font1 = new Font("Arial", 10.0f);
try {
    byte charset = font1.GdiCharSet;
} finally {
  if (font1 != null) {
    ((IDisposable)font1).Dispose();
  }
}

So, the Dispose method will be called before the next statement following the using block.

The person writing the article might have it confused with how an object will be garbage collected some time after it's not used any more, or how disposable objects that are not disposed have their finalizer called by a background thread.

笔芯 2024-10-06 18:06:44

使用(非泛指).NET 5 和 xunit 将理论与事实结合起来。

虽然 Try..Catch..Finally 构造会在最终断言之前释放对象,但 using 块不会。

public class DisposableUnitTests
{

    private class DisposableObject : IDisposable
    {

        public bool Disposed { get; set; }

        protected virtual void Dispose( bool disposing )
        {
            if ( disposing )
                this.Disposed = true;
        }

        public void Dispose()
        {
            this.Dispose( true );
        }
    }

    [Fact]
    public void CanTryCatchFinallyDispose()
    {
        DisposableObject Target = new DisposableObject();
        try
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        catch ( Exception )
        {

            throw;
        }
        finally
        {
            ((IDisposable)Target)?.Dispose();
        }
        Assert.True( Target.Disposed , $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

    [Fact]
    public void CanUsingBlockDispose()
    {
        using DisposableObject Target = new DisposableObject();
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        Assert.False( Target.Disposed, $"Using block not disposing {nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

}

Theory meets facts using (no pan intended) .NET 5 and xunit.

While the Try..Catch..Finally construct disposes the object before the final assertion, the Using block does not.

public class DisposableUnitTests
{

    private class DisposableObject : IDisposable
    {

        public bool Disposed { get; set; }

        protected virtual void Dispose( bool disposing )
        {
            if ( disposing )
                this.Disposed = true;
        }

        public void Dispose()
        {
            this.Dispose( true );
        }
    }

    [Fact]
    public void CanTryCatchFinallyDispose()
    {
        DisposableObject Target = new DisposableObject();
        try
        {
            Assert.False( Target.Disposed, 
quot;{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        catch ( Exception )
        {

            throw;
        }
        finally
        {
            ((IDisposable)Target)?.Dispose();
        }
        Assert.True( Target.Disposed , 
quot;{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

    [Fact]
    public void CanUsingBlockDispose()
    {
        using DisposableObject Target = new DisposableObject();
        {
            Assert.False( Target.Disposed, 
quot;{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        Assert.False( Target.Disposed, 
quot;Using block not disposing {nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文