我正在使用boost singletons(来自序列化)。
例如,有一些类继承boost::serialization::singleton
。他们每个人都有这样的定义(在h文件中):
#define appManager ApplicationManager::get_const_instance()
class ApplicationManager: public boost::serialization::singleton<ApplicationManager> { ... };
我必须每次更新(近17毫秒)从该类调用一些方法,例如200次。所以代码就像:
for (int i=0; i < 200; ++i)
appManager.get_some_var();
我在函数调用堆栈中查看gprof
并且每次都看到boost::get_const_instance
个调用。也许,在发布模式编译器中会优化这个吗?
我的想法是制作一些全局变量,如:
ApplicationManager &handle = ApplicationManager::get_const_instance();
并使用handle
,因此每次都不会调用get_const_instnace
。是吗?
答案 0 :(得分:4)
不是使用Singleton anti-pattern,而是使用全局变量。这更诚实。
Singleton的主要好处是你想要延迟初始化,或者对初始化顺序进行更细粒度的控制,而不是全局变量允许你。看起来这些事情都不适合你,所以只需使用全局。
就个人而言,我认为具有全局变量或单身人士的设计几乎肯定会被打破。但是对于每个h(是/呃)拥有。
如果您倾向于使用Singleton,那么您提出的性能问题很有意思,但可能不是问题,因为函数调用开销可能小于100ns。正如所指出的,你应该剖析。如果它真的让你感兴趣,那么在循环之前存储一个对Singleton的本地引用:
ApplicationManager &myAppManager = appManager;
for (int i=0; i < 200; ++i)
myAppManager.get_some_var();
顺便说一句,以这种方式使用#define
是一个严重的错误。几乎所有将预处理器用于基于编译时标志的条件编译以外的任何情况的情况都可能很糟糕。 Boost确实广泛使用了预处理器,但主要是为了克服C ++的限制。不要效仿它。
最后,该功能可能正在做一些重要的事情。 Singletons的get_instance
方法的一个工作是避免让多个线程同时初始化同一个Singleton。对于全局变量,这应该不是问题,因为它们应该在您启动任何线程之前初始化。
答案 1 :(得分:2)
真的有问题吗?我的意思是,你的应用程序真的会受到这种行为的影响吗?
我会鄙视这样的解决方案,因为在所有效果中,你正在抵制Singleton模式的一个好处,即避免全局变量。如果你想使用全局变量,那么根本不要使用Singleton,对吗?
答案 2 :(得分:1)
是的,这当然是一种可能的解决方案。我不完全确定在幕后使用它的单身人士有什么好处;你可以在代码中查看自己。
单例模式就像在大多数方面创建全局对象和访问全局对象一样。存在一些差异:
1)单例对象实例在首次访问之前不会创建,而全局对象是在程序启动时创建的。 2)因为单例对象在首次访问之前不会创建,所以它实际上是在程序运行时创建的。因此,当构造函数实际运行时,单例实例可以访问程序中的其他完全构造的对象。 3)因为您通过getInstance()方法访问单例(boost的get_const_instance方法),所以执行该方法调用会产生一些开销。
因此,如果您不关心实际创建单例的时间,并且可以在程序启动时创建它,您可以使用全局变量并访问它。如果你真的需要程序启动后创建的单例,那么你需要单例。在这种情况下,您可以抓取并保持对get_const_instance()返回的对象的引用并使用该引用。
虽然你应该知道,但过去的一些事情让我感到困惑。您实际上是在获取单例所拥有的对象的引用。你不拥有那个对象。
1)不要编写会导致析构函数执行的代码(例如,在返回的引用上使用共享指针),或编写任何其他可能导致对象最终处于错误状态的代码。
2)在多线程应用程序中,如果对象可能被多个线程使用,请注意正确锁定对象中的字段。
3)在多线程应用程序中,确保在卸载程序之前,所有保留对对象引用的线程都会终止。我见过一个单例代码驻留在一个DLL库中的情况;保存引用的线程存在于另一个DLL库中。程序结束时,线程仍处于活动状态。保存单例代码的DLL首先被卸载;仍然活着的线程试图对单身人士的对象做一些事情并导致崩溃。
答案 3 :(得分:1)
单身人士在你想要控制进程或应用程序范围内某些内容访问级别的情况下有其优势,超出了全局变量可以以更优雅的方式实现的目的。
但是,库中提供的大多数单例对象都将被设计为确保一定程度的线程安全性,并且很可能通过互斥锁或某种可影响性能的其他关键部分锁定实例的访问权。
对于性能至关重要的游戏或3D应用程序,如果不考虑线程安全性并获得一些性能,您可能需要考虑制作自己的轻量级单例。