在最近的一个项目中,我不得不创建一个Singleton类,经过大量的Google搜索后,我想出了这个模板类的定义。我们的想法是从这个模板类派生出来,并使派生类的构造函数受保护/私有。它似乎运作良好,但我只在一个项目中使用它一个类,所以我希望你们中的一些人可以指出我是否在实现中犯了错误。这是:
/**
* @brief
* Singleton design pattern implementation using a dynamically allocated singleton instance.
*
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
* design pattern and require lazy initialization of the singleton object. The default
* implementation is not thread-safe, however, the derived classes can make it so by reinitializing
* the function pointers SingletonDynamic<T>::pfnLockMutex, SingletonDynamic<T>::pfnUnlockMutex
* and SingletonDynamic<T>::pfnMemoryBarrier. The member function pointers are initialized by
* default to point to placeholder functions that do not perform any function. The derived class
* must provide alternate implementations for SingletonDynamic<T>::lock_mutex(),
* SingletonDynamic<T>::unlock_mutex() and SingletonDynamic<T>::memory_barrier() respectively
* and reinitialize the respective function pointer members to these alternate implementations.
*
* @tparam T
* The type name of the derived (singleton) class
*
* @note The derived class must have a no-throw default constructor and a no-throw destructor.
* @note The derived class must list this class as a friend, since, by necessity, the derived class'
* constructors must be protected / private.
*/
template< typename T >
class SingletonDynamic
{
public:
/**
* Factory function for vending mutable references to the sole instance of the singleton object.
*
* @return A mutable reference to the one and only instance of the singleton object.
*/
static T &instance()
{
return *SingletonDynamic< T >::get_instance();
}
/**
* Factory function for vending constant references to the sole instance of the singleton object.
*
* @return A constant reference to the one and only instance of the singleton object.
*/
static const T &const_instance()
{
return *SingletonDynamic< T >::get_instance();
}
protected:
/** Default constructor */
SingletonDynamic() {}
/** Destructor */
virtual ~SingletonDynamic()
{
delete SingletonDynamic< T >::pInstance_;
}
/** Defines an alias for a function pointer type for executing functions related to thread-safety */
typedef void(*coherence_callback_type)();
/**
* Pointer to a function that will lock a mutex denying access to threads other that the current
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnLockMutex;
/**
* Pointer to a function that will unlock a mutex allowing access to other threads
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnUnlockMutex;
/**
* Pointer to a function that executes a memory barrier instruction that prevents the compiler
* from reordering reads and writes across this boundary.
*
* @note The function must have the signature void foo()
* @note The derived class must never set this variable to NULL, doing so will cause a crash. The
* default value must be left unchanged if this functionality is not desired.
*/
static coherence_callback_type pfnMemoryBarrier;
private:
/** The sole instance of the singleton object */
static T *pInstance_;
/** Flag indicating whether the singleton object has been created */
static volatile bool flag_;
/** Private copy constructor to prevent copy construction */
SingletonDynamic( SingletonDynamic const & );
/** Private operator to prevent assignment */
SingletonDynamic &operator=( SingletonDynamic const & );
/**
* Fetches a pointer to the singleton object, after creating it if necessary
*
* @return A pointer to the one and only instance of the singleton object.
*/
static T *get_instance()
{
if( SingletonDynamic< T >::flag_ == false ) {
/* acquire lock */
(*SingletonDynamic< T >::pfnLockMutex)();
if( SingletonDynamic< T >::pInstance_ == NULL ) {
pInstance_ = new T();
}
/* release lock */
(*SingletonDynamic< T >::pfnUnlockMutex)();
/* enforce all prior I/O to be completed */
(*SingletonDynamic< T >::pfnMemoryBarrier)();
SingletonDynamic< T >::flag_ = true;
return SingletonDynamic< T >::pInstance_;
} else {
/* enforce all prior I/O to be completed */
(*SingletonDynamic< T >::pfnMemoryBarrier)();
return SingletonDynamic< T >::pInstance_;
}
}
/**
* Placeholder function for locking a mutex, thereby preventing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* implementation if this functionality is desired.
*/
inline static void lock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for unlocking a mutex, thereby allowing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* implementation if this functionality is desired.
*/
inline static void unlock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for executing a memory barrier instruction, thereby preventing the
* compiler from reordering read and writes across this boundary. This default implementation does
* not perform any function, the derived class must provide an implementation if this
* functionality is desired.
*/
inline static void memory_barrier()
{
/* default implementation does nothing */
return;
}
};
/* Initialize the singleton instance pointer */
template< typename T >
T *SingletonDynamic<T>::pInstance_ = NULL;
/* Initialize the singleton flag */
template< typename T >
volatile bool SingletonDynamic<T>::flag_ = false;
/* Initialize the function pointer that locks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnLockMutex
= &SingletonDynamic<T>::lock_mutex;
/* Initialize the function pointer that unlocks the mutex */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnUnlockMutex
= &SingletonDynamic<T>::unlock_mutex;
/* Initialize the function pointer that executes the memory barrier instruction */
template< typename T >
typename SingletonDynamic<T>::coherence_callback_type SingletonDynamic<T>::pfnMemoryBarrier
= &SingletonDynamic<T>::memory_barrier;
我特别担心头文件中的静态成员初始化以及当从SingleDynamic派生的类的头文件包含在多个文件中时是否会导致多个定义错误。我已经试过了,它似乎工作,但我无法弄清楚它的工作原理:)。
提前致谢, 与Ashish。
编辑:使用已接受解决方案中建议的基于策略的设计修改了实施。
/**
* This is the default ConcurrencyPolicy implementation for the SingletonDynamic class. This
* implementation does not provide thread-safety and is merely a placeholder. Classes deriving from
* SingletonDynamic must provide alternate ConcurrencyPolicy implementations if thread-safety is
* desired.
*/
struct DefaultSingletonConcurrencyPolicy
{
/**
* Placeholder function for locking a mutex, thereby preventing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* alternate implementation if this functionality is desired.
*/
static void lock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for unlocking a mutex, thereby allowing access to other threads. This
* default implementation does not perform any function, the derived class must provide an
* alternate implementation if this functionality is desired.
*/
static void unlock_mutex()
{
/* default implementation does nothing */
return;
}
/**
* Placeholder function for executing a memory barrier instruction, thereby preventing the
* compiler from reordering read and writes across this boundary. This default implementation does
* not perform any function, the derived class must provide an alternate implementation if this
* functionality is desired.
*/
static void memory_barrier()
{
/* default implementation does nothing */
return;
}
};
/**
* @brief
* Singleton design pattern implementation using a dynamically allocated singleton instance.
*
* The SingletonDynamic class is intended for use as a base for classes implementing the Singleton
* design pattern and that dynamic allocation of the singleton object. The default implementation
* is not thread-safe; however, the class uses a policy-based design pattern that allows the derived
* classes to achieve threaad-safety by providing an alternate implementation of the
* ConcurrencyPolicy.
*
* @tparam T
* The type name of the derived (singleton) class
* @tparam ConcurrencyPolicy
* The policy implementation for providing thread-safety
*
* @note The derived class must have a no-throw default constructor and a no-throw destructor.
* @note The derived class must list this class as a friend, since, by necessity, the derived class'
* constructors must be protected / private.
*/
template< typename T, typename ConcurrencyPolicy = DefaultSingletonConcurrencyPolicy >
class SingletonDynamic : public ConcurrencyPolicy
{
public:
/**
* Factory function for vending mutable references to the sole instance of the singleton object.
*
* @return A mutable reference to the one and only instance of the singleton object.
*/
static T &instance()
{
return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
}
/**
* Factory function for vending constant references to the sole instance of the singleton object.
*
* @return A constant reference to the one and only instance of the singleton object.
*/
static const T &const_instance()
{
return *SingletonDynamic< T, ConcurrencyPolicy >::get_instance();
}
protected:
/** Default constructor */
SingletonDynamic() {}
/** Destructor */
virtual ~SingletonDynamic()
{
delete SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
}
private:
/** The sole instance of the singleton object */
static T *pInstance_;
/** Flag indicating whether the singleton object has been created */
static volatile bool flag_;
/** Private copy constructor to prevent copy construction */
SingletonDynamic( SingletonDynamic const & );
/** Private operator to prevent assignment */
SingletonDynamic &operator=( SingletonDynamic const & );
/**
* Fetches a pointer to the singleton object, after creating it if necessary
*
* @return A pointer to the one and only instance of the singleton object.
*/
static T *get_instance()
{
if( SingletonDynamic< T, ConcurrencyPolicy >::flag_ == false ) {
/* acquire lock */
ConcurrencyPolicy::lock_mutex();
/* create the singleton object if this is the first time */
if( SingletonDynamic< T, ConcurrencyPolicy >::pInstance_ == NULL ) {
pInstance_ = new T();
}
/* release lock */
ConcurrencyPolicy::unlock_mutex();
/* enforce all prior I/O to be completed */
ConcurrencyPolicy::memory_barrier();
/* set flag to indicate singleton has been created */
SingletonDynamic< T, ConcurrencyPolicy >::flag_ = true;
return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
} else {
/* enforce all prior I/O to be completed */
ConcurrencyPolicy::memory_barrier();
return SingletonDynamic< T, ConcurrencyPolicy >::pInstance_;
}
}
};
/* Initialize the singleton instance pointer */
template< typename T, typename ConcurrencyPolicy >
T *SingletonDynamic< T , ConcurrencyPolicy >::pInstance_ = NULL;
/* Initialize the singleton flag */
template< typename T, typename ConcurrencyPolicy >
volatile bool SingletonDynamic< T , ConcurrencyPolicy >::flag_ = false;
答案 0 :(得分:3)
模板类的静态成员必须在头文件中初始化,最近的C ++编译器及其链接器必须正确处理。
但你是对的,一些非常古老的编译器存在问题。
在这些情况下,对于使用单例模板的每种类型,在任意编译单元中初始化静态成员一次是一种解决方法。
gcc文档具有相同的详细信息:http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html。
我记得一个嵌入式项目(不久前),旧的编译器仍然在使用,而且一个静默地创建了模板的静态成员的多个实例。 当它存在一个单身人士时,显然是一个非常糟糕的主意....
更糟糕的是,库中唯一使用的Singleton(第三方框架)是一些通常以相同方式初始化的Configuration对象,因此只有在运行时更改配置时才会出现bug。 花了几天的时间来追踪这个错误,直到我们最终在反汇编中看到在不同的内存区域访问“相同”的成员。
答案 1 :(得分:1)
答案 2 :(得分:1)
此处并发相关代码的正确性很难评估。在我看来,实施工作有点过于聪明了。
OTOH,所有与并发相关的代码基本上都有它后面的存根,什么都不做。如果在非线程环境中使用它,我认为应该没问题。但是,我也认为你的担心是有根据的。那些静态成员的外部定义似乎违反了one definition rule。
就个人而言,我认为应该重写此模板以将并发内容作为模板本身的策略参数,并要求派生类在适当的.cpp文件中声明自己的pInstance
版本
其他人建议在静态局部变量的初始化方面依赖编译器特定的行为。我不认为这是一个可怕的建议,但是当你不能依赖编译器做正确的事情时,有一个选项可能会很好。
答案 3 :(得分:1)
目前无法在C ++的多线程环境中懒洋洋地创建Singleton。
许多大师(其中包括Herb Sutter)已经承认标准的当前状态并不能保证任何东西。有各种编译器的hacks,并且boost为此目的提供once
工具,但它是编译器特定指令的杂乱集合......它不是标准C ++(线程不知道)。
当前工作的唯一解决方案(根据标准)是在启动多个线程之前初始化Singleton,或者在保证只有一个线程访问它的过程的一部分中初始化Singleton。
C ++ 0x将线程带入标准,并且特别保证即使在存在多个线程的情况下也只创建一次本地静态变量(在多个同时调用的情况下全部阻塞直到创建结束)。因此以下方法:
static MyType& Instance() { static Instance MExemplar; return MExemplar; }
有效,在这种情况下根本不需要单例模板类。