【问题标题】:Singleton factory, sort of辛格尔顿工厂,有点像
【发布时间】:2010-02-01 00:33:50
【问题描述】:

对不起,如果这已在其他地方得到回答...我发现了很多类似的帖子,但不一样。

我想确保一次只存在一个对象的一个​​实例,但我不希望该对象在其自然生命周期之后保留,因为它可能是单例模式。

我正在编写一些代码,其中每分钟都会触发列表处理(通过我无法控制的外部代码)。目前,我每次只创建一个新的“处理”对象,当它超出范围时,它会按照正常情况被销毁。但是,有时处理时间可能超过一分钟,因此下一个触发器将在新线程中创建处理类的第二个实例。

现在,我想要一种机制,一次只能存在一个实例……比如说,某种工厂一次只允许一个对象。对工厂的第二次调用将返回 null,而不是一个新对象。

到目前为止,我的(糟糕的)解决方案是将工厂类型对象作为处理器类的嵌套类:

class XmlJobListProcessor
{
    private static volatile bool instanceExists = false;

    public static class SingletonFactory
    {
        private static object lockObj = new object();

        public static XmlJobListProcessor CreateListProcessor()
        {
            if (!instanceExists)
            {
                lock (lockObj)
                {
                    if (!instanceExists)
                    {
                        instanceExists = true;
                        return new XmlJobListProcessor();
                    }
                    return null;
                }
            }
            return null;
        }
    }

    private XmlJobListProcessor() { }
    ....
    }

我正在考虑为 XmlJobListProcessor 类编写一个显式析构函数,将“instanceExists”字段重置为 false。

我意识到这是一个非常糟糕的设计。工厂本身应该是一个类...它只是嵌套的,因此它和实例析构函数都可以访问 volatile 布尔值...

谁有更好的方法来做到这一点?干杯

【问题讨论】:

  • 为什么只需要创建一个完整的工厂来创建一个东西?
  • 为什么不重写 InitializeLifeTime 以返回 null 以使其保持“活动”?
  • @Pierre - 'Factory' 可能用词不当 - 其想法是将类的实例数限制为一个,即使多个线程可能尝试同时创建它们。并且在不保留对该对象的任何引用的情况下执行此操作,以便它可以具有正常的范围内-范围外生命周期。
  • 假设:您希望在这里一次一个任务处理的一个原因是处理列表的每一“轮”都会更改列表内容:因此多线程不可用。免责声明:这个一般领域是我没有重要经验的领域,所以请用“一粒盐”来看待这个问题,并请原谅一个可能非常无知的问题:但是这里与排队的想法是否存在自然的“共鸣”完成任务?
  • 一次处理一个的原因是任务类有依赖关系,它们有自己的依赖关系......这个想法是为了防止不必要的使用服务器资源。一次一个就好了。你是对的;队列确实是一种想法,但可能不接受“重复任务”(因为它们都是相同的任务,不需要排队),或者容量为 1。跨度>

标签: c# object-lifetime


【解决方案1】:

我知道 .NET 4 没有被广泛使用,但最终它会被广泛使用,并且您将拥有:

private static readonly Lazy<XmlJobListProcessor> _instance =
    new Lazy<XmlJobListProcessor>(() => new XmlJobListProcessor());

然后您可以通过_instance.Value 访问它,它在第一次被请求时被初始化。

【讨论】:

  • 哦,听起来不错。如果不是因为我正在进行 SharePoint 2010 beta 2 开发,我会使用它,并且它在针对 .NET 4 方面存在“问题”。叹息。很高兴知道,但伙计。 :D
【解决方案2】:

您的原始示例使用双重检查锁定,应不惜一切代价避免。

请参阅msdn Singleton implementation,了解如何正确初始化单例。

【讨论】:

【解决方案3】:

只需制作一个并保留它,不要每分钟破坏和创建它

“最小化运动部件”

【讨论】:

  • 我想这真的是一种限制服务器上资源使用的可怕方法。我想将实例数限制为一个,但要确保它一次只在一个线程中使用(因为处理可以每分钟触发一次),这就是我想避免单例模式的原因......我确实想要一次一个实例,但一次只能访问一个线程......工厂的想法很简单,比如“如果一个已经存在,你就不能再有另一个实例......再过一分钟再回来试试。 ..”。
  • 另外,通过在不需要时不保留实例(大概是每分钟的 70%),内存被释放用于其他事情。
  • 我无法想象你正在“释放”很多东西。把它想象成停止和启动你的汽车。与连续停车和启动相比,您可以让车辆怠速运行更长的时间。类比是停止和开始某事需要“更多的工作”。每次你杀死实例时,它都会受到 GC 的审查......这会占用 GC 做一些“更重要”的时间。
  • @[oolong-singy]:在大多数现代平台上,内存分配和销毁是执行成本最高的操作。除非你的单例占用了几兆字节(也许即使它占用了!),你最好让它闲置。如果单线程是主要关注点,请使用信号量或锁。
【解决方案4】:

我会实例化该类并保留它。当然我不会使用析构函数(如果你的意思是 ~myInstance() )......这会增加 GC 时间。另外,如果一个过程花费的时间超过一分钟,如果你只返回一个空值,你会如何处理假设要处理的数据?

保持实例处于活动状态,并可能构建一个缓冲区机制以在处理器类忙时继续接收输入。您可以查看:

if ( isBusy == true )
{
     // add data to bottom of buffer
}
else
{
     // call processing
}

【讨论】:

  • 嗯,看来你和上面的观点差不多……都好好学习。 :D。至于“如果您只返回一个空值,您将如何处理假设要处理的数据?” ...没有数据,更像是“执行相同的任务 X,如果它还没有运行”,在哪里执行该任务,我实例化一个类并调用它的方法之一。
  • 我能不能把类设为静态,给它一个静态类锁定对象,然后让“执行任务”方法类似于: public static void ProcessList(SPList xmlJobList) { lock (lockObj) { 。 .. 做东西 } }。我想如果处理时间超过一分钟,这只会堆叠等待线程。但是当下一个等待线程获得锁时,将无事可做,因为任务的类型是“对于此列表中的每个未处理项,处理它...”
  • 好吧,我对“lock(myObject)”的理解是用于多线程目的。如果我没记错的话,静态实例会推断线程安全。我只是实例化处理类,并实现bool IsBusy
  • 我之前的评论很糟糕......我看到你是多线程的。您是手动线程还是(我的偏好)使用 .BeginInvoke(...)???
  • 就像我说的,我无法控制的系统(Sharepoint Timer 服务)正在触发多线程请求。我无法控制线程的创建。我要做的就是限制多个线程尝试访问同一个对象的可能性。至少,我假设 SP 正在触发新线程上的请求....
【解决方案5】:

我同意每个人关于不重新实例化处理器对象的观点和 BillW 关于队列的观点,所以这是我的混搭解决方案:

public static class PRManager
{
    private static XmlJobListProcessor instance = new XmlJobListProcessor();
    private static object lockobj = new object();

    public static void ProcessList(SPList list)
    {
         bool acquired = Monitor.TryEnter(lockobj);
            try
            {
                if (acquired)
                {
                    instance.ProcessList(list);
                }
            }
            catch (ArgumentNullException)
            {
            }
            finally
            {
                Monitor.Exit(lockobj);
            }
    }
}

处理器作为静态成员长期保留(这里,长期对象保留不是问题,因为它没有状态变量等)如果在 lockObj 上获得了锁,则不会处理请求调用线程将继续其业务。

为反馈的家伙干杯。 Stackoverflow 将确保我的实习! ;D

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    • 2016-08-23
    • 1970-01-01
    • 2021-07-31
    • 2016-04-28
    相关资源
    最近更新 更多