C# 4.0 懒惰<;T>;仅限LazyThreadSafeMode.PublicationOnly和IDisposable

C# 4.0 懒惰<;T>;仅限LazyThreadSafeMode.PublicationOnly和IDisposable,c#-4.0,idisposable,lazythreadsafetymode,C# 4.0,Idisposable,Lazythreadsafetymode,今天我在玩Lazy,发现了一个有趣的案例(在我看来) 仅限出版物: 当多个线程试图同时初始化一个惰性实例时,所有线程都可以运行初始化方法。。。由竞争线程创建的任何T实例都将被丢弃 如果我们查看Lazy.LazyInitValue()的代码,我们会发现没有检查IDisposable实现,解析可能会泄漏到这里: case LazyThreadSafetyMode.PublicationOnly: boxed = this.CreateValue(); if (I

今天我在玩Lazy
,发现了一个有趣的案例(在我看来)

  • 仅限出版物:

    当多个线程试图同时初始化一个惰性实例时,所有线程都可以运行初始化方法。。。由竞争线程创建的任何T实例都将被丢弃

    如果我们查看Lazy
    .LazyInitValue()的代码,我们会发现没有检查IDisposable实现,解析可能会泄漏到这里:

     case LazyThreadSafetyMode.PublicationOnly:
            boxed = this.CreateValue();
            if (Interlocked.CompareExchange(ref this.m_boxed, boxed, null) != null)
            {  
               //* boxed.Dispose(); -> see below.
               boxed = (Boxed<T>) this.m_boxed;
            }
            break;
    

要回答第一个问题,如果类“正确”实现了IDisposable,那么就没有资源泄漏的危险。但是,在垃圾回收发生之前,非托管资源可能会一直处于未释放状态

考虑以下应用程序:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;

namespace LazyInit {
    class DisposableClass : IDisposable {
        private IntPtr _nativeResource = Marshal.AllocHGlobal(100);
        private System.IO.MemoryStream _managedResource = new System.IO.MemoryStream();
        public string ThreadName { get; set; }

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

        ~DisposableClass() {
            Console.WriteLine("Disposing object created on thread " + this.ThreadName);
            Dispose(false);
        }

        private void Dispose(bool disposing) {
            if (disposing) {
                // free managed resources
                if (_managedResource != null) {
                    _managedResource.Dispose();
                    _managedResource = null;
                }
            }
            // free native resources if there are any.
            if (_nativeResource != IntPtr.Zero) {
                Marshal.FreeHGlobal(_nativeResource);
                _nativeResource = IntPtr.Zero;
            }
        }
    }

    static class Program {
        private static Lazy<DisposableClass> _lazy;

        [STAThread]
        static void Main() {
            List<Thread> t1 = new List<Thread>();

            for (int u = 2, i = 0; i <= u; i++)
                t1.Add(new Thread(new ThreadStart(InitializeLazyClass)) { Name = i.ToString() });
            t1.ForEach(t => t.Start());
            t1.ForEach(t => t.Join());

            Console.WriteLine("The winning thread was " + _lazy.Value.ThreadName);
            Console.WriteLine("Garbage collecting...");
            GC.Collect();
            Thread.Sleep(2000);
            Console.WriteLine("Application exiting...");
        }

        static void InitializeLazyClass() {
            _lazy = new Lazy<DisposableClass>(LazyThreadSafetyMode.PublicationOnly);
            _lazy.Value.ThreadName = Thread.CurrentThread.Name;
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Runtime.InteropServices;
使用系统线程;
名称空间LazyInit{
类别可处置类别:IDisposable{
私有IntPtr _nativeResource=Marshal.AllocHGlobal(100);
private System.IO.MemoryStream _managedResource=new System.IO.MemoryStream();
公共字符串ThreadName{get;set;}
公共空间处置(){
处置(真实);
总干事(本);
}
~DisposableClass(){
WriteLine(“正在处理线程上创建的对象”+this.ThreadName);
处置(虚假);
}
私有无效处置(bool处置){
如果(处置){
//免费管理的资源
如果(_managedResource!=null){
_managedResource.Dispose();
_managedResource=null;
}
}
//免费本地资源(如果有)。
如果(_nativeResource!=IntPtr.Zero){
自由全球元帅(_nativeResource);
_nativeResource=IntPtr.Zero;
}
}
}
静态类程序{
私有静态惰性(u Lazy);;
[状态线程]
静态void Main(){
列表t1=新列表();
对于(intu=2,i=0;it.Start());
t1.ForEach(t=>t.Join());
WriteLine(“获胜的线程是”+\u lazy.Value.ThreadName);
控制台.WriteLine(“垃圾收集…”);
GC.Collect();
《睡眠》(2000年);
WriteLine(“应用程序正在退出…”);
}
静态void InitializeLazyClass(){
_lazy=新的lazy(仅LazyThreadSafetyMode.PublicationOnly);
_lazy.Value.ThreadName=Thread.CurrentThread.Name;
}
}
}
它使用
LazyThreadSafetyMode.PublicationOnly
,创建三个线程,每个线程实例化
Lazy
,然后退出

输出如下所示:

获胜的线程是1

垃圾收集

正在处理线程2上创建的对象

正在处理线程0上创建的对象

应用程序正在退出

正在处理线程1上创建的对象

如问题中所述,
Lazy.LazyInitValue()
不检查IDisposable,并且没有显式调用
Dispose()
,但仍然最终处理了所有三个对象;处理两个对象是因为它们超出范围并被垃圾收集,第三个对象是在应用程序退出时处理的。发生这种情况的原因是,我们正在使用销毁所有托管对象时调用的析构函数/终结器,并反过来使用它来确保释放非托管资源

至于第二个问题,为什么没有设立可识别的支票,大家都在猜测。也许他们没有设想在实例化时分配非托管资源

进一步阅读:

有关如何正确实现IDisposable的更多信息,请查看MSDN(我的示例有一半来自MSDN)


此外,还有一篇优秀的SO文章,它给出了我所见过的最好的解释,为什么IDisposable应该以这种方式实现。

这在.NET上被忽略了,这就是为什么我同时提供和。顺便说一下,我的计算机模拟了这种行为(请看代码的末尾)。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;

namespace LazyInit {
    class DisposableClass : IDisposable {
        private IntPtr _nativeResource = Marshal.AllocHGlobal(100);
        private System.IO.MemoryStream _managedResource = new System.IO.MemoryStream();
        public string ThreadName { get; set; }

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

        ~DisposableClass() {
            Console.WriteLine("Disposing object created on thread " + this.ThreadName);
            Dispose(false);
        }

        private void Dispose(bool disposing) {
            if (disposing) {
                // free managed resources
                if (_managedResource != null) {
                    _managedResource.Dispose();
                    _managedResource = null;
                }
            }
            // free native resources if there are any.
            if (_nativeResource != IntPtr.Zero) {
                Marshal.FreeHGlobal(_nativeResource);
                _nativeResource = IntPtr.Zero;
            }
        }
    }

    static class Program {
        private static Lazy<DisposableClass> _lazy;

        [STAThread]
        static void Main() {
            List<Thread> t1 = new List<Thread>();

            for (int u = 2, i = 0; i <= u; i++)
                t1.Add(new Thread(new ThreadStart(InitializeLazyClass)) { Name = i.ToString() });
            t1.ForEach(t => t.Start());
            t1.ForEach(t => t.Join());

            Console.WriteLine("The winning thread was " + _lazy.Value.ThreadName);
            Console.WriteLine("Garbage collecting...");
            GC.Collect();
            Thread.Sleep(2000);
            Console.WriteLine("Application exiting...");
        }

        static void InitializeLazyClass() {
            _lazy = new Lazy<DisposableClass>(LazyThreadSafetyMode.PublicationOnly);
            _lazy.Value.ThreadName = Thread.CurrentThread.Name;
        }
    }
}