单例模式的分类
- 懒汉模式
- 饿汉模式
饿汉模式
在全局区创建单例对象,程序编译时这个单例就已经存在
饿汉模式是线程安全的
懒汉模式
在程序第一次需要使用实例的时候才创建这个单例
懒汉模式并非现场安全
看如下一段代码:
static std::shared_ptr<ShoppingManager> GetInstance()
{
if (s_Instance == nullptr)
{
std::unique_lock<std::mutex> lock(s_ShoppingMutex);
if (s_Instance == nullptr)
{
s_Instance = std::shared_ptr<ShoppingManager>(new ShoppingManager());
}
}
return s_Instance;
}
这段代码中,多个线程可能同时检测到s_Instance == nullptr条件为真,从而创建多个实例
同时这段代码使用了双检锁的技术,避免多个线程都要获取锁而带来的性能开销
懒汉模式代码实现
用智能指针+双检锁优化
有一个悬而未决的问题:下面这段代码不能将析构函数设为私有,否则会报错
class ShoppingManager
{
public:
static std::shared_ptr<ShoppingManager> GetInstance()
{
if (s_Instance == nullptr)
{
std::unique_lock<std::mutex> lock(s_ShoppingMutex);
if (s_Instance == nullptr)
{
s_Instance = std::shared_ptr<ShoppingManager>(new ShoppingManager());
}
}
return s_Instance;
}
void PushItem(const std::string& item_name, int num)
{
std::unique_lock<std::mutex> lock(s_VectorMutex);
m_ShoppingCart.push_back(std::make_pair(item_name, num));
}
void PrintInfo()
{
for (const auto& elem : m_ShoppingCart)
{
std::cout << elem.first << " " << elem.second << std::endl;
}
}
private:
ShoppingManager()
{
m_ShoppingCart.reserve(100);
}
ShoppingManager& operator=(const ShoppingManager& other) = delete;
ShoppingManager(const ShoppingManager& other) = delete;
private:
static std::shared_ptr<ShoppingManager> s_Instance;
std::vector<std::pair<std::string, int>> m_ShoppingCart;
static std::mutex s_ShoppingMutex;
static std::mutex s_VectorMutex;
};
std::shared_ptr<ShoppingManager> ShoppingManager::s_Instance = nullptr;
std::mutex ShoppingManager::s_ShoppingMutex;
std::mutex ShoppingManager::s_VectorMutex;
int main()
{
std::string item_name;
int num;
while (std::cin >> item_name >> num)
{
ShoppingManager::GetInstance()->PushItem(item_name, num);
}
ShoppingManager::GetInstance()->PrintInfo();
return 0;
}
用智能指针+static局部变量优化
static局部变量保证了线程安全
避免使用锁而带来的开销
static std::shared_ptr<ShoppingManager> GetInstance()
{
// 使用 C++11 的线程安全静态局部变量初始化单例
static std::shared_ptr<ShoppingManager> s_Instance(new ShoppingManager());
return s_Instance;
}
使用std::call_once优化
避免使用锁而带来的开销
class ShoppingManager
{
public:
static std::shared_ptr<ShoppingManager> GetInstance()
{
std::call_once(flag, []{
s_Instance = std::shared_ptr<ShoppingManager>(new ShoppingManager());
});
return s_Instance;
}
void PushItem(const std::string& item_name, int num)
{
std::unique_lock<std::mutex> lock(s_VectorMutex);
m_ShoppingCart.push_back(std::make_pair(item_name, num));
}
void PrintInfo()
{
for (const auto& elem : m_ShoppingCart)
{
std::cout << elem.first << " " << elem.second << std::endl;
}
}
private:
ShoppingManager()
{
m_ShoppingCart.reserve(100);
}
ShoppingManager& operator=(const ShoppingManager& other) = delete;
ShoppingManager(const ShoppingManager& other) = delete;
private:
static std::shared_ptr<ShoppingManager> s_Instance;
std::vector<std::pair<std::string, int>> m_ShoppingCart;
static std::mutex s_VectorMutex;
static std::once_flag flag;
};
std::shared_ptr<ShoppingManager> ShoppingManager::s_Instance = nullptr;
std::mutex ShoppingManager::s_VectorMutex;
std::once_flag ShoppingManager::flag;
用单例类中嵌套的垃圾回收类来释放内存
这种方法相比上面的方法可以隐蔽析构函数接口,同时要注意垃圾回收时一定要创建对象而不是指针!
请注意必须要有类外初始化的代码
ShoppingManager::GarbageCollection ShoppingManager::GCobject;
否则甚至编译器都不会创建这个对象,那么这个对象的析构函数也不会执行,也就没有垃圾回收!
class ShoppingManager
{
public:
static ShoppingManager* GetInstance()
{
std::call_once(flag, [] {
s_Instance = new ShoppingManager();
});
return s_Instance;
}
void PushItem(const std::string& item_name, int num)
{
std::unique_lock<std::mutex> lock(s_VectorMutex);
m_ShoppingCart.emplace_back(item_name, num);
}
void PrintInfo()
{
std::unique_lock<std::mutex> lock(s_VectorMutex); // Add lock to ensure thread safety
for (const auto& elem : m_ShoppingCart)
{
std::cout << elem.first << " " << elem.second << std::endl;
}
}
private:
ShoppingManager()
{
m_ShoppingCart.reserve(100);
}
~ShoppingManager(){}
ShoppingManager& operator=(const ShoppingManager& other) = delete;
ShoppingManager(const ShoppingManager& other) = delete;
private:
static ShoppingManager* s_Instance;
std::vector<std::pair<std::string, int>> m_ShoppingCart;
static std::mutex s_VectorMutex;
static std::once_flag flag;
private:
class GarbageCollection
{
public:
GarbageCollection(){}
~GarbageCollection()
{
if (s_Instance)
{
delete s_Instance;
s_Instance = nullptr;
}
}
};
static GarbageCollection GCobject;
};
ShoppingManager* ShoppingManager::s_Instance = nullptr;
std::mutex ShoppingManager::s_VectorMutex;
std::once_flag ShoppingManager::flag;
ShoppingManager::GarbageCollection ShoppingManager::GCobject;