带一次性物品的C#Singleton

带一次性物品的C#Singleton,c#,thread-safety,singleton,dispose,C#,Thread Safety,Singleton,Dispose,假设我有一个单例,它在创建时将资源加载到内存中,并在调用其方法时对数据执行操作。 现在假设,我希望能够告诉Singleton释放这些资源,因为我不希望在不久的将来使用它们,但也能够在时机成熟时重新加载这些资源。我希望这一切都是线程安全的 解决这个问题的最好办法是什么 这个例子行得通吗 //单例实现 ... 私有可识别资源; 私人布尔资源; 私有IDisposable资源{ get=>resource??抛出新的CustomException(); } //方法A 公共图书馆A(){ var re

假设我有一个单例,它在创建时将资源加载到内存中,并在调用其方法时对数据执行操作。 现在假设,我希望能够告诉Singleton释放这些资源,因为我不希望在不久的将来使用它们,但也能够在时机成熟时重新加载这些资源。我希望这一切都是线程安全的

解决这个问题的最好办法是什么

这个例子行得通吗

//单例实现
...
私有可识别资源;
私人布尔资源;
私有IDisposable资源{
get=>resource??抛出新的CustomException();
}
//方法A
公共图书馆A(){
var resource=resource;//如果资源为null,则引发CustomException
//做事
}
//方法B
公共空间B(){
var资源=资源;
//做事
}
公共无效释放资源(){
if(资源!=null)
锁(这个锁){
//resource.Dispose();
资源=空;
}
}
公共资源(){
如果(!loadingResources&&resource==null)
锁(这个锁)
如果(!loadingResources&&resource==null)
{
loadingResources=true;
//加载资源
resource=CreateResource();
loadingResources=false;
}
}

我建议将资源处理与实际使用分开。假设资源需要处置,这可能类似于:

    public class DisposableWrapper<T> where T : IDisposable
    {
        private readonly Func<T> resourceFactory;
        private T resource;
        private bool constructed;
        private object lockObj = new object();
        private int currentUsers = 0;

        public DisposableWrapper(Func<T> resourceFactory)
        {
            this.resourceFactory = resourceFactory;
        }

        public O Run<O>(Func<T, O> func)
        {
            lock (lockObj)
            {
                if (!constructed)
                {
                    resource = resourceFactory();
                    constructed = true;
                }
                currentUsers++;
            }

            try
            {
                return func(resource);
            }
            catch
            {
                return default;
            }
            finally
            {
                Interlocked.Decrement(ref currentUsers);
            }
        }

        public void Run(Action<T> action)
        {
            lock (lockObj)
            {
                if (!constructed)
                {
                    resource = resourceFactory();
                    constructed = true;
                }
                currentUsers++;
            }

            try
            {
                action(resource);
            }
            finally
            {
                Interlocked.Decrement(ref currentUsers);
            }
        }

        public bool TryRelease()
        {
            lock (lockObj)
            {
                if (currentUsers == 0 && constructed)
                {
                    constructed = false;
                    resource.Dispose();
                    resource = default;
                    return true;
                }
                return false;
            }
        }
    }
公共类DisposableWrapper,其中T:IDisposable
{
私有只读Func resourceFactory;
私人旅游资源;
私人建筑;
私有对象lockObj=新对象();
私有int currentUsers=0;
公共可处置包装器(Func resourceFactory)
{
this.resourceFactory=resourceFactory;
}
公共O运行(Func Func)
{
锁(lockObj)
{
如果(!构造)
{
resource=resourceFactory();
构造=真;
}
当前用户++;
}
尝试
{
返回func(资源);
}
抓住
{
返回默认值;
}
最后
{
联锁减量(参考电流用户);
}
}
公开作废运行(操作)
{
锁(lockObj)
{
如果(!构造)
{
resource=resourceFactory();
构造=真;
}
当前用户++;
}
尝试
{
行动(资源);
}
最后
{
联锁减量(参考电流用户);
}
}
公共图书馆
{
锁(lockObj)
{
如果(currentUsers==0&&C)
{
构造=假;
resource.Dispose();
资源=默认值;
返回true;
}
返回false;
}
}
}

如果资源不需要处理,我建议改为使用。释放资源只意味着用新对象替换现有的惰性对象。让垃圾收集器清理旧对象。

您考虑过另一个重定向级别吗?也就是说,如果单例服务持有对实际服务的引用,它可以将所有调用重定向到该服务。在“ReleaseResources”中,您所要做的就是取消一个引用,GC会注意到这一点,并且持续的使用可以优雅地结束thair任务。然后,所有新调用要么失败(因为“secondary”实例引用为null),要么导致“secondary”服务的重新创建。仔细想想:听起来很像工厂模式的一些变体。这是一个有趣的建议。假设我们在Singleton中有一个数据类实例。您将在每个方法的开头创建一个引用,即
var data=this.data
然后检查它是否为空。也许,以防万一,也可以使用线程锁使数据属性线程安全?看看这个::“模式[…]可能不安全。有时,它可以被视为反模式。”这也是相关的:@TheodorZoulias,我知道Java过去在这方面有问题,但引用您提供的stackoverflow源代码:“双重检查锁定现在在Java和C#中都能工作”,虽然我可以使用
Lazy
,但Wiki引用了一句话:“在.NETFramework4.0中,引入了Lazy类,它默认内部使用双重检查锁定”。我说过,如果你最近有一篇好文章(stackoverflow帖子是2008年的),我会读一读(注:我使用的是Net Core 3.1)在Fildor的评论中,我更改了示例。你认为这样行吗?@guiorgy否,在资源被处置后仍然可以使用它。并且你需要在每个方法中复制资源检查代码。“并且你需要在每个方法中复制资源检查代码”。你可以重新介绍它(请参见编辑)。如果我正确理解了您的解决方案,这实际上是资源的包装。对资源的任何操作都是通过代理运行进行的。如果我不想返回结果,您可能只需要传递一个委托(例如Func)然后返回它返回的对象。我唯一关心的是,它总是使用线程锁。你认为有可能减少吗?首先,你不需要
联锁。增量
,因为它已经在线程锁
锁(lockObj)中了
@Guiorgy是的,这形成了一个包装器。如果需要返回值,可以添加一个变量