转载自:Zhongtian‘s Technical Notes,如侵联删。
单例模式是使用最广泛的设计模式之一,其目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1 Eager Singleton
class Singleton {
public:
static Singleton& GetInstance() {
return instance;
}
private:
Singleton() {};
~Singleton() {};
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton instance;
};
由于在 main 函数之前初始化,所以该实现方式没有线程安全的问题。但是潜在问题在于 no-local static 对象(函数外的 static 对象)在不同编译单元中的初始化顺序是未定义的。也即 static Singleton instance;
和 static Singleton& GetInstance()
二者的初始化顺序不确定,如果在初始化完成之前调用 GetInstance()
方法会返回一个未定义的实例。
2 Lazy Singleton
2.1 线程不安全
class Singleton {
public:
static Singleton* GetInstance() {
if (instance == nullptr)
instance = new Singleton();
return instance;
}
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* instance;
};
注意:
- C++ 规定 const 静态类成员可以直接初始化,其他非 const 的静态类成员需要在类声明以外初始化,我们一般选择在类的实现文件中初始化。
- 静态成员在 cpp 文件中也声明一下,否则编译时会提示 undefined reference。
2.2 线程安全,存在 memory order 潜在问题:双检测锁模式(Double-Checked Locking Pattern, DCLP)
class Singleton {
public:
static Singleton* GetInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> mutx(some_mutex);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* instance;
std::mutex some_mutex;
};
如果第一次检测不做则会每次获取都加锁,在实例已经创建的情况下这是没有必要的。在第一次检测实例是否被创建和加锁的操作之间,可能有另一个线程创建了实例,所以第二次检测也是必不可少的。
2.3 线程安全:atomic
DCLP 其实也存在问题。在某些内存模型中或者是由于编译器的优化以及运行时优化等等原因,使得 instance 虽然已经不是 nullptr 但是其所指对象还没有完成构造,这种情况下,另一个线程如果调用 GetInstance() 就有可能使用到一个不完全初始化的对象。在 C++11 没有出来的时候,只能靠插入两个 memory barrier(内存屏障)来解决这个错误,但是 C++11 引进了 memory model,提供了 atomic 实现内存的同步访问,即不同线程总是获取对象修改前或修改后的值,无法在对象修改期间获得该对象。
class Singleton {
public:
static Singleton* GetInstance() {
Singleton* tmp = instance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> mutx(some_mutex);
tmp = instance; // tmp = instance.load(memory_order_seq_cst);
if (instance == nullptr) {
tmp = new Singleton();
instance = tmp; // instance.store(tmp, memory_order_seq_cst);
}
}
return instance;
}
private:
Singleton() {}
~Singleton() {}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static std::atomic<Singleton*> instance;
std::mutex some_mutex;
};
3 最佳实践,Meyers’ Singleton
3.1 实践方式
class Singleton {
public:
static Singleton& GetInstance() {
static Singleton inst;
return inst;
}
};
单例模式的两个特性以如下方式被保证:
- 线程安全性:C++11 规定了局部静态变量在多线程条件下的初始化行为,要求编译器保证了局部静态变量的线程安全性
- 单次初始化:C++11 规定了局部静态变量在代码第一次执行到变量声明的地方时初始化,局部静态变量的特性保证了其唯一性
另外,C++11 还提供了 std::call_once 函数,也可以用来实现多线程安全的单例对象初始化。
3.2 可能存在的问题
在这样一种情况下:
- 有一个动态库和一个调用该动态库的主程序
- Meyers Singleton 类定义在动态库中
- 在动态库的 .cpp 文件和主程序中分别调用 GetInstance() 方法
这两次调用的单例其实并不是同一个对象,造成这种现象的原因是每个动态库都有自己的静态数据实例。
解决方法主要有:
- 避免在库中调用单例
- 使用 thread locale storage
- 构建方式换成静态库
参考
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++ – 单例模式
原文链接:https://www.stubbornhuang.com/2767/
发布于:2023年08月25日 14:39:29
修改于:2023年08月25日 14:39:29
当前分类随机文章推荐
- C++ – Unicode编码下的全角字符转半角字符 阅读2712次,点赞0次
- C++ – 字节数组byte[]或者unsigned char[]与long long的相互转换 阅读2177次,点赞0次
- C++ - Windows系统获取桌面路径 阅读460次,点赞0次
- C++ - 对字符串和图片进行base64编解码 阅读1068次,点赞0次
- C++ - 使用cuda api获取本机显卡数量和显卡信息 阅读458次,点赞0次
- C++ - 在两个互有依赖关系的类中使用std::shared_ptr和std::weak_ptr进行内存管理 阅读990次,点赞0次
- C++读取Shp文件并将Shp转化为DXF 阅读3514次,点赞1次
- C++ - Windows系统使用C++切换音频默认输出设备 阅读78次,点赞0次
- C++11/std::shared_ptr - 循环引用问题 阅读4609次,点赞0次
- C++ - 使用Websocket++编写客户端连接WebSocket服务器并进行通信 阅读5124次,点赞3次
全站随机文章推荐
- Duilib - Edit编辑控件输入文字时编辑框背景颜色不是所设置的背景颜色的问题 阅读782次,点赞1次
- C++11 - 委托机制的实现TinyDelegate 阅读1722次,点赞0次
- 资源分享 - Graphics Programming Methods 英文高清PDF下载 阅读2187次,点赞0次
- FFmpeg - ./configure编译参数全部总结和整理 阅读2642次,点赞3次
- Pytorch - 检测CUDA、cuDNN以及GPU版本的Pytorch是否安装成功、GPU显存测试 阅读4781次,点赞1次
- 怎么找CVPR历年会议全部论文? 阅读737次,点赞0次
- 计算几何 - 二维笛卡尔坐标系中,计算二维点绕任意中心点旋转任意角度的结果 阅读1611次,点赞0次
- OpenVINO - 在Windows系统上配置OpenVINO C++开发环境 阅读271次,点赞0次
- 资源分享 - Geometric tools for computer graphics(Philip J. Schneider, and David H. Eberly)英文高清PDF下载 阅读3884次,点赞0次
- FFmpeg - 关于ffmpeg avcodec_open2函数失败的问题 阅读3669次,点赞0次
评论
169