modelo de rendimiento de objetos
- La orientación a objetos resuelve muy bien el problema "abstracto", pero es inevitable pagar un cierto precio. En términos generales, el costo de la orientación a objetos es en su mayoría insignificante, pero en algunos casos, el costo de la orientación a objetos debe manejarse con cuidado.
- patrón típico
- Sigleton
- peso mosca
-------------------------------------------------- --------------------------------------------
patrón único
1. Motivación
- En los sistemas de software, a menudo hay clases especiales de este tipo que deben garantizarse para tener solo una instancia en el sistema para garantizar su corrección lógica y buena eficiencia.
- ¿Cómo eludir los constructores convencionales y proporcionar un mecanismo para garantizar que una clase tenga solo una instancia?
- Esto debería ser responsabilidad del diseñador de la clase, no del usuario.
2. Código
class Sigleton{
private:
Sigleton();
Sigleton(const Sigleton& other);
public:
static Sigleton*getInstance();
static Sigleton* m_instance;
};
Sigleton* Sigleton::m_instance = nullptr;
// 线程非安全版本
Sigleton* Sigleton::getInstance(){
if (m_instance == nullptr){
m_instance = new Sigleton();
}
return m_instance;
}
// 线程安全,但锁的代价过高
Sigleton* Sigleton::getInstance(){
Lock lock;
if (m_instance == nullptr){
m_instance = new Sigleton();
}
return m_instance;
}
// 双检查锁,但由于内存读写reorder不安全
Sigleton* Sigleton::getInstance(){
if (m_instance == nullptr){
Lock lock;
if (m_instance == nullptr){
// 下面New的大概步骤
// Step1. 先分配内存
// Step2. 调用Sigleton构造器,对刚才分配的内存进行初始化
// Step3. 将刚才分配的地址赋值给m_instance
// 但是在CPU指令级别,上面的步骤有可能会被reorder
// 编译器优化后,有可能会变成:
// Step1 -> Step3 -> Step2
// 如果此时执行到Step3,切换到另外一个线程,最外面的判断将是非空,然后直接去使用这个还没有构造的m_instance,导致错误
m_instance = new Sigleton(); // 2000年左右才被一个java领域的专家发现这个BUG
}
}
return m_instance;
}
// C++11版本之后的跨平台版本
std::atomic<Sigleton*> Sigleton::m_instance = nullptr;
std::mutex Sigleton::m_mutex;
Sigleton* Sigleton::getInstance(){
Sigleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire); // 获取内存fence
if (tmp == nullptr){
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr){
tmp = new Sigleton();
std::atomic_thread_fence(std::memory_order_relaxed); // 释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
3. Definición del esquema
Garantiza que solo haya una instancia de una clase y proporciona un punto de acceso global a esa instancia.
---《Patrones de diseño》GOF
4. Estructura
5. Resumen
- Los constructores de instancias en el patrón Sigleton se pueden configurar como protegidos para permitir la creación de subclases.
- El modo Sigleton generalmente no es compatible con el constructor de copia y la interfaz Clone, porque esto puede dar lugar a múltiples instancias de objetos.