设计模式篇之单例模式
单例模式在是日常开发工作中最长使用到的一种设计模式,可能很多开发者应在程序中使用,但是却不清楚是单例模式,比如Go中使用借助init方法和全局变量实现数据库连接初始化,是不是都这么写过?下边就详细介绍一下单例模式的实现和适用场景。
1. 适用场景
单例模式的主要功能是限制一个类型(class/struct)全局只能有一个实例对象,不可以重复创建。基于这个核心的功能就可以得出在需要多个实例的场景下是不适用的,那么哪些场景是单实例场景?如下:
- 数据库连接初始化:单机场单应用场景下通常只需要一个数据库实例即可,一个实例不代表只有一个连接,通常会有一个连接池来真正的管理连接。
- 配置初始化:程序启动时会从配置文件加载配置信息,或者在运行过程中从远程配置中心获取配置信息然后初始化配置。
2. 懒汉模式和饿汉模式
根据实例初始化的时间和阶段可以区分成两种模式:懒汉模式和饿汉模式。
饿汉模式:在程序启动时即开始执行单例模式程序,创建全局唯一的实例。这个叫法也很生动形象,饿汉就是很饥饿,需要立马吃东西,这个东西就是实例。
懒汉模式:在程序需要的时候才创建全局唯一的类型实例,不需要在程序初始化阶段执行。这一模式需要特别注意并发安全问题,多个线程/协程同时初始化实例信息时只能有一个成功。
3. 代码实现
(1). 饿汉模式
Go实现中可以借助init()和全局变量来实现,对外暴露一个公共方法来获取全局唯一的实例,注意全局变量和其他的struct/class字段需要设置为私有。
package main
import "fmt"
type Config struct {
path string
maxOpenCount int
}
var conf *Config
func init() {
conf = &Config{
path: "/test/ttt/ff",
maxOpenCount: 10,
}
}
func GetConfig() *Config {
return conf
}
func main() {
cf := GetConfig()
fmt.Printf("配置信息为: %+v\n", cf)
}
(2). 懒汉模式
懒汉模式需要有一个全局的实例对象和标识是否已经初始化的状态标识位,由于懒汉模式下会出现多个线程/协程并发初始化实例的问题,所以在Go中需要借助sync/Mutex + sync/atomic来实现,也可以使用sync/once原语来实现,C++中可以使用pthread_once()方法保证只有一个线程可以真正执行,类似于go中的sync/once。
- go实现(sync/Mutex+sync/atomic)
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type Config struct {
path string
maxOpenCount int
}
var initialized uint32
var conf *Config
var mu sync.Mutex
func GetInstance() *Config {
if atomic.LoadUint32(&initialized) == 1 {
fmt.Println("已经初始化过了,无需再次初始化!")
return conf
}
mu.Lock()
defer mu.Unlock()
if atomic.LoadUint32(&initialized) == 0 {
conf = &Config{
path: "/test/ttt/ff",
maxOpenCount: 10,
}
atomic.CompareAndSwapUint32(&initialized, 0, 1)
fmt.Println("初始化成功!")
}
return conf
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
cf := GetInstance()
fmt.Printf("配置信息: %+v\n", cf)
wg.Done()
}
wg.Wait()
}
- go实现(sync/once)
type Config struct {
path string
maxOpenCount int
}
var conf *Config
func GetInstanceOnce() *Config {
var once sync.Once
once.Do(func() {
conf = &Config{
path: "/test/ttt/ff",
maxOpenCount: 10,
}
fmt.Println("初始化完成!")
})
return conf
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
cf := GetInstanceOnce()
fmt.Printf("配置信息: %+v\n", cf)
wg.Done()
}
wg.Wait()
}
- C++实现(pthread_once)
定义一个不可拷贝和初始化的基类。
#ifndef LIVE_BROADCAST_NONCOPYABLE_H
#define LIVE_BROADCAST_NONCOPYABLE_H
namespace lb {
namespace pattern {
class NonCopyable {
protected:
NonCopyable(){};
~NonCopyable(){};
NonCopyable(const NonCopyable&) = delete;
NonCopyable &operator = (const NonCopyable&) = delete;
};
}
}
#endif //LIVE_BROADCAST_NONCOPYABLE_H
创建一个单例类型,继承自基类NonCopyable,对外提供两个公共方法:
Instance()初始化实例对象。
DeleteInstance()在程序结束时释放资源。
#include <pthread.h>
#include "NonCopyable.h"
#ifndef LIVE_BROADCAST_SINGLETON_H
#define LIVE_BROADCAST_SINGLETON_H
namespace lb {
namespace pattern {
template <typename T>
class Singleton:public NonCopyable {
public:
Singleton() = delete;
~Singleton() = delete;
static T*& Instance() {
// 保证只有一个线程可以执行成功
pthread_once(&ponce_, Singleton<T>::init);
return value_;
}
static void DeleteInstance() {
if (value_) {
delete value_;
value_ = nullptr;
}
}
private:
static pthread_once_t ponce_;
static T* value_;
static void init() {
if (!value_) {
value_ = new T();
}
}
};
template <typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template <typename T>
T * Singleton<T>::value_ = nullptr;
}
}
#endif //LIVE_BROADCAST_SINGLETON_H
测试程序
#include <iostream>
#include "NonCopyable.h"
#include "Singleton.h"
class TS: public lb::pattern::NonCopyable {
public:
TS() = default;
~TS() = default;
static void Println() {
std::cout << "this is a singleton instance" << std::endl;
std::cout << "Next.." << std::endl;
}
};
#define Cy lb::pattern::Singleton<TS>
int main(int argc, const char ** argv) {
int i;
for (i = 0; i < 10; i++) {
TS* instance = Cy::Instance();
instance->Println();
}
Cy::DeleteInstance();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。