设计模式之单例模式 (模板 智能指针 删除器) 实现【懒汉】
2016-03-29 15:05
489 查看
上一篇关于单例模式的实现 主要是Doubke-Check Locking。
但是多线程环境下并不能保证完全线程安全。
这篇文章实现的单例:
懒汉模式 的单例
基于模板和继承
线程安全
使用智能指针 防止内存泄露
效率相对较高
实现代码如下:
贴一下运行结果图:
注意到代码中针对原子锁的使用的代码。
第一次调用res = 0 不会进入if语句块 不会打印 "res --- Thread ID = "这句话
线程切换后 会打印那句话 我们可以看到 剩余的两个线程都打印了。
这正好验证了我们的思路。
第一个线程创建单例返回后 其余线程才会退出等待。
另外 内存分配完成后,调用getInstance()获取单例。
然后调用print()打印 "Hello, this is Manager"那句话
多次运行结果可能不太一样。
因为 内存分配完成后
CPU的所有权现在不确定在哪个线程上,获取到线程都可以去执行print()函数。
这时候不存在线程等待和轮询的问题。
可以在print()方法打印语句中 输出当前线程ID 进行调试。
欢迎大家批评指正~
但是多线程环境下并不能保证完全线程安全。
这篇文章实现的单例:
懒汉模式 的单例
基于模板和继承
线程安全
使用智能指针 防止内存泄露
效率相对较高
实现代码如下:
#include <iostream> #include <memory> #include <windows.h> #include <process.h> using namespace std; template <class T> class CSingletonPtr { // 私有删除器 private: class Deleter { public: // 重载()操作符 void operator()(T* p) { delete p; } }; private: static tr1::shared_ptr<T> m_pInstance; // 智能指针 避免内存泄露 private: CSingletonPtr(const CSingletonPtr&){} CSingletonPtr& operator=(const CSingletonPtr&){} protected: // 需要继承 所以一定要声明为public CSingletonPtr() { cout << "CSingletonPtr begin construct" << endl; ::Sleep(1000); cout << "CSingletonPtr end construct" << endl; } virtual ~CSingletonPtr() { cout << "CSingletonPtr destrcut" << endl; } public: // 返回引用 避免拷贝构造和析构 提高效率 static T& getInstance() { // static 表明变量是跨线程共享的 // volatile 表示不进行编译器优化 static volatile long lock = 0; /* if (lock == 0) { lock = 1; } 分析上面这段代码, 考虑下面这种情况: 线程1正在执行 if(lock == 0)的判断 此时切换到线程2 线程2正在执行lock = 1; 因此会出问题 这种情况需要避免。 使用InterlockedCompareExchange函数解决上面出现的问题 一个线程调用该函数 那么会立即锁定变量内存地址 其他线程不可同时访问 注:只能锁定32位(4字节)变量 */ int res = 0; // 只有第一次调用InterlockedCompareExchange的线程才会跳过下面的if块 创建单例的实例 并返回 // 使用编译器提供的本质函数 _InterlockedCompareExchange 提高效率 if (res = ::_InterlockedCompareExchange(&lock, 1, 0) != 0) { cout << res << "---" << " Thread ID = " << ::GetCurrentThreadId() << endl; // 不是第一次调用该函数的线程 会一直轮询等待 // 直至第一次调用该函数的线程创建好单例并返回单例实例 才会结束轮询 while (lock != 2) { ::Sleep(0); } return *m_pInstance.get(); } tr1::shared_ptr<T> tmp(new T(), T::Deleter()); m_pInstance = tmp; lock = 2; // 内存分配好了就让其他线程停止等待 cout << "内存分配初始化完成" << endl; return *m_pInstance.get(); } }; // 模板静态成员初始化*** template <class T> tr1::shared_ptr<T> CSingletonPtr<T>::m_pInstance = NULL; // 实例化模板 class Manager : public CSingletonPtr<Manager> { // 访问权限授权给基类 friend class CSingletonPtr<Manager>; protected: Manager() { cout << "Manager begin construct" << endl; ::Sleep(500); m_count = 0; cout << "Manager end construct" << endl; } ~Manager() { cout << "Manager destrcut" << endl; } public: void print() { cout << "Hello, this is Manager " << m_count++ << endl; } private: int m_count; }; //-----------------多线程测试----------------- unsigned int __stdcall threadFun(void*) { cout << "Current Thread ID = " << ::GetCurrentThreadId() << endl; Manager::getInstance().print(); return 0; } void testMultiThread() { for (int i = 0; i < 3; ++i) { uintptr_t th = ::_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL); ::CloseHandle((HANDLE)th); } } int main(void) { testMultiThread(); getchar(); return 0; }
贴一下运行结果图:
注意到代码中针对原子锁的使用的代码。
第一次调用res = 0 不会进入if语句块 不会打印 "res --- Thread ID = "这句话
线程切换后 会打印那句话 我们可以看到 剩余的两个线程都打印了。
这正好验证了我们的思路。
第一个线程创建单例返回后 其余线程才会退出等待。
另外 内存分配完成后,调用getInstance()获取单例。
然后调用print()打印 "Hello, this is Manager"那句话
多次运行结果可能不太一样。
因为 内存分配完成后
CPU的所有权现在不确定在哪个线程上,获取到线程都可以去执行print()函数。
这时候不存在线程等待和轮询的问题。
可以在print()方法打印语句中 输出当前线程ID 进行调试。
欢迎大家批评指正~
相关文章推荐
- 字符串匹配KMP算法
- Cordova从服务器更新客户端的JS文件
- 为什么你应该尝试全栈?
- 如何在SSH中创建一个定时器
- php 读正在写的文件
- rsync+sersync+nfs高可用
- android studio创建aidl文件
- jupyter与spark kernel结合的notebook安装及使用
- Genymotion解决无法下载镜像问题
- Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解
- JS实现子元素scroll父元素容器不跟随滚动
- WebSocket与STOMP介绍
- 测试3.29作业 叶乔菲 3013218151
- Oracle密码过期
- 字体类形:font-family, font-style
- 关于vmware虚拟机硬件里没有软盘驱动器,而操作系统里还有的解决方法
- 指针数组与数组指针
- Leetcode 100. Same Tree
- C#静态构造函数
- php ajax实现文件上传进度条