建设网站如果赚钱,电脑可以做服务器部署网站吗,网盘网页版,摄影网页制作1、单例设计模式 单例设计模式#xff0c;使用的频率比较高#xff0c;整个项目中某个特殊的类对象只能创建一个 并且该类只对外暴露一个public方法用来获得这个对象。 单例设计模式又分懒汉式和饿汉式#xff0c;同时对于懒汉式在多线程并发的情况下存在线程安全问题 饿汉…1、单例设计模式 单例设计模式使用的频率比较高整个项目中某个特殊的类对象只能创建一个 并且该类只对外暴露一个public方法用来获得这个对象。 单例设计模式又分懒汉式和饿汉式同时对于懒汉式在多线程并发的情况下存在线程安全问题 饿汉式类加载的准备阶段就会将static变量、代码块进行实例化最后只暴露一个public方法获得实例对象。 懒汉式当需要用到的时候再去加载这个对象。这时多线程的情况下可能存在线程安全问题 对于饿汉式这里不做具体的解释本节只讨论多线程与懒汉式的线程安全问题
2、单线程下的懒汉模式
2.1、单例对象的创建
将类指针对象进行静态私有化并且在类外初始化这个对象为空静态能保证的是这个对象属于这个类不属于任何一个对象私有化空构造器防止可以实例化对象对外暴露一个public方法获取该对象如果在获取时发现该对象为空那么进行实例化否则直接返回因此可以看到实例化只有一次多次获取到的对象的地址属于同一个
class Single_Instance {
private:static Single_Instance *instance;Single_Instance() {}
public:static Single_Instance *get_Instance(){if(instance NULL){instance new Single_Instance();}return instance;}void func(){std::cout func(), instance instance std::endl;}
};
Single_Instance *Single_Instance::instance NULL;void test1()
{Single_Instance *instance1 Single_Instance::get_Instance();Single_Instance *instance2 Single_Instance::get_Instance();instance1-func();instance2-func();
}# 输出
func(), instance 0x5652eefede70
func(), instance 0x5652eefede702.2、单例对象的析构
很明显上面的代码缺少一个析构函数并且似乎无从下手找一个合适的时机对其进行析构只能等待程序运行结束操作系统回收其实可以通过内部类的方式进行析构 首先在单例类内部进行私有化一个内部类对外暴露的public获取instance的对象接口在new实例化对象的时候创建一个内部类静态成员内部类静态成员的好处是只有一份当作用域结束时内部类就会负责析构掉主类的静态成员对象
class Single_Instance {
private:static Single_Instance *instance;Single_Instance() {}class inner_class {public:~inner_class(){if(Single_Instance::instance){delete Single_Instance::instance;Single_Instance::instance NULL;std::cout inner_class::~inner_class(), 析构Single_Instance::instance对象 std::endl;}}};
public:static Single_Instance *get_Instance(){if(instance NULL){instance new Single_Instance();static inner_class innerClass;}return instance;}void func(){std::cout func(), instance instance std::endl;}
};
Single_Instance *Single_Instance::instance NULL;void test1()
{Single_Instance *instance1 Single_Instance::get_Instance();Single_Instance *instance2 Single_Instance::get_Instance();instance1-func();instance2-func();
}
#输出
func(), instance 0x558eb768de70
func(), instance 0x558eb768de70
inner_class::~inner_class(), 析构Single_Instance::instance对象3、单例模式与多线程 单例模式的对象可能会被多个线程使用但是又必须保证这个单例的对象只有一份 不能重复创建、也必须保证这个对象在多线程使用过程中不会因为创建而产生数据安全问题即多线程抢占的创建这一个对象
class Single_Instance {
private:static Single_Instance *instance;Single_Instance() {}class inner_class {public:~inner_class(){if(Single_Instance::instance){delete Single_Instance::instance;Single_Instance::instance NULL;std::cout inner_class::~inner_class(), 析构Single_Instance::instance对象 std::endl;}}};
public:static Single_Instance *get_Instance(){if(instance NULL){instance new Single_Instance();static inner_class innerClass;}return instance;}void func(){std::cout func(), instance instance std::endl;}
};
Single_Instance *Single_Instance::instance NULL;void thread_func()
{std::cout 子线程开始执行了 std::endl;Single_Instance *instance Single_Instance::get_Instance();std::cout thread_func, instance instance std::endl;std::cout 子线程执行结束了 std::endl;
}void test2()
{std::thread mythread1(thread_func);std::thread mythread2(thread_func);std::thread mythread3(thread_func);std::thread mythread4(thread_func);mythread1.join();mythread2.join();mythread3.join();mythread4.join();
}可以看到实例化不止一个单例对象这一现象违反了单例的思想因此需要在多线程抢占创建时进行互斥mutex
3.1、解决方案一
使用互斥量的方式对线程访问获取对象进行阻塞但是不难发现问题其实这个对象只创建一次之后的访问单纯的获取这个对象也要进行加锁逐个排队访问临界区这一现象导致效率极低
std::mutex mutex_lock;
class Single_Instance {
private:static Single_Instance *instance;Single_Instance() {}class inner_class {public:~inner_class(){if(Single_Instance::instance){delete Single_Instance::instance;Single_Instance::instance NULL;std::cout inner_class::~inner_class(), 析构Single_Instance::instance对象 std::endl;}}};
public:static Single_Instance *get_Instance(){std::unique_lockstd::mutex uniqueLock(mutex_lock);if(instance NULL){instance new Single_Instance();static inner_class innerClass;}return instance;}void func(){std::cout func(), instance instance std::endl;}
};
Single_Instance *Single_Instance::instance NULL;void thread_func()
{std::cout 子线程开始执行了 std::endl;Single_Instance *instance Single_Instance::get_Instance();std::cout thread_func, instance instance std::endl;std::cout 子线程执行结束了 std::endl;
}void test2()
{std::thread mythread1(thread_func);std::thread mythread2(thread_func);std::thread mythread3(thread_func);std::thread mythread4(thread_func);mythread1.join();mythread2.join();mythread3.join();mythread4.join();
}3.2、解决方式二
双重检查机制DCL进行绝对安全解决
双重检查 首先在锁外面加入一个if判断判断这个对象是否存在如果存在就没有必要上锁创建直接返回即可如果对象不存在首选进行加锁然后在if判断对象是否存在这个if的意义在于当多个线程阻塞在mutex锁头上时突然有一个线程1创建好了那么阻塞在mutex锁头上的线程2、3、4…都不用再继续创建因此在加一个if判断
这里还需要解释一下volatile关键字 volatile关键字的作用是防止cpu指令重排序重排序的意思就是干一件事123的顺序cpu可能重排序为132 为什么需要防止指令重排序因为对象的new过程分为三部曲 1分配内存空间、2执行构造方法初始化对象、3将这个对象指向这个空间 由于程序运行CPU会进行指令的重排序如果执行的指令是132顺序A线程执行完13之后并没有完成对象的初始化、而这时候转到B线程B线程认为对象已经实例化完毕、其实对象并没有完成初始化产生错误 但这个问题在C11中已经禁止了重排序因此不需要使用volatile关键字但在Java和一些其他语言中可能有Java中这个关键字是针对即时编译器JIT进行指令重排序的
static Single_Instance *get_Instance(){if(instance NULL){std::unique_lockstd::mutex uniqueLock(mutex_lock);if(instance NULL){instance new Single_Instance();static inner_class innerClass;}}return instance;
}只需要把上面的代码改成这个样子即可
4、std::call_once() std::call_once()是C11引入的函数该函数的功能就是保证一个方法只会被调用一次。 参数二一个函数名func 参数一std::once_flag一个标记本质是一个结构体。该标志可以用于标记参数二该函数是否已经调用过了 参数三参数二函数的参数 std::call_once()具有互斥量的这种能力且效率上比mutex互斥量效率更高因此也可以使用这个函数对单例的线程安全进行保证 当call_once调用过一次之后std::once_flag将会被修改标记已调用那么之后都不会在调用 下面看个代码举例可以看到create_Instance()函数中对于这个函数只执行了一次完全ojbk。
class Single_Instance {
private:static Single_Instance *instance;static std::once_flag instance_flag;Single_Instance() {}class inner_class {public:~inner_class(){if(Single_Instance::instance){delete Single_Instance::instance;Single_Instance::instance NULL;std::cout inner_class::~inner_class(), 析构Single_Instance::instance对象 std::endl;}}};
public:static void create_Instance(){instance new Single_Instance();static inner_class innerClass;}static Single_Instance *get_Instance(){std::call_once(instance_flag, create_Instance);return instance;}void func(){std::cout func(), instance instance std::endl;}
};
Single_Instance *Single_Instance::instance NULL;
std::once_flag Single_Instance::instance_flag;void thread_func()
{std::cout 子线程开始执行了 std::endl;Single_Instance *instance Single_Instance::get_Instance();std::cout thread_func, instance instance std::endl;std::cout 子线程执行结束了 std::endl;
}void test3()
{std::thread mythread1(thread_func);std::thread mythread2(thread_func);std::thread mythread3(thread_func);std::thread mythread4(thread_func);mythread1.join();mythread2.join();mythread3.join();mythread4.join();
}