这是啥设计模式-单例模式

召回算法中有一个叫做I2I的召回方式,给定一个Item,返回跟这个Item相似的topK个Item,这种相似关系一般在离线就计算好了,以KV的方式存储下来

<item1, [item2, item3,item4]>
<item2, [item1, item5,item6]>
...

我们使用的时候也加载成kv的格式就可以了

1. 基础实现

我们要提供这样一个数据,同样需要定义一个接口

using KvPair = std::unordered_map<std::string, std::vector<std::string>>;
class Kv{
public:
	Kv() = default;
	void update(const std::string& kvPath) {
		auto kv = std::make_shared<KvPair>();
		kv->emplace("item1", std::vector<std::string>{"item1", "item2", "item3"});
		kv->emplace("item2", std::vector<std::string>{"item1", "item4"});
		kv->emplace("item3", std::vector<std::string>{"item1"});
		{
    		// 使用 lock_guard 锁定互斥锁
    		std::lock_guard<std::mutex> lock(mtx);
    		m_kv.swap(kv);
		}
	}
	std::vector<std::string> getTopK(const std::string& item) {
		auto iter = m_kv->find(item);
		if(iter != m_kv->end()) {
			return iter->second;
		}
		return std::vector<std::string>();
	}
	~Kv() = default;
private:
	std::shared_ptr<KvPair> m_kv = std::make_shared<KvPair>();
	// 创建一个互斥锁
    std::mutex mtx;
}

我们实现了两个方法,一个是数据更新的方法,一个是获取TopK的方法。客户端调用也非常的简单

auto kv = new Kv();
Kv->update('kv.pb');
auto items = Kv->getItems("item1");

2. 多处调用

现在有好几个地方都会调用这份kv数据,我们简单实现一下,第一个是test1.cpp文件

// test1.cpp
void test1() {
	auto kv = new Kv();
	Kv->update('kv.pb');
	auto items = Kv->getItems("item1");
}

第二个地方是test2.cpp文件

// test2.cpp
void test2() {
	auto kv = new Kv();
	Kv->update('kv.pb');
	auto items = Kv->getItems("item1");
}

这么实现的问题非常的明显,就是每个地方调用都需要加载一次数据,但是这份数据不会发生变化,谁调用都是一样的。

3. 单例模式

一个简单的想法就是让这个类对象只有一个,首先构造函数不能暴露出来,不然就可以定义多个对象了

class Kv{
private:
   // 构造函数和析构函数都定义为private,防止被外部调用
	Kv() = default;
	~Kv() = default;
	// 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
    Kv(const Kv &kv) = delete;
    const Kv &operator=(const Kv &kv) = delete;
public:
	// 类方法,类直接调用
	static Kv *GetInstance();
}

问题是构造函数都变成私有的了,该如何实例化这个对象呢?我们可以定义一个函数来代替构造函数,在这个函数里面我们设置一个标识符,表示这个对象是否已经初始化过了,如果初始化过了,就返回之前构造出来的对象,否则就进行构造初始化

Kv* Kv::kvInstance{nullptr};
std::mutex Kv::mutexKvInstance;
Kv* Kv::GetInstance()
{
	std::lock_guard<std::mutex> lock(mutexKvInstance);
    if (kvInstance== nullptr) // 如果是nullptr说明还没有被初始化过
    {
        kvInstance= new Kv();
    }
    return kvInstance; // 返回唯一的对象
}

还有一种更有有趣的实现方法,极力推荐

// 注意:不能返回指针的引用,否则存在外部被修改的风险!
Kv& Kv::GetInstance()
{
    /**
     * 局部静态特性的方式实现单实例。静态局部变量只在当前函数内有效,其他函数无法访问。
     * 静态局部变量只在第一次被调用的时候初始化,也存储在静态存储区,生命周期从第一次被初始化起至程序结束止。
     */
    static Kv kv;
    return kv;
}

4. 单例模式python

import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        with Singleton._instance_lock:
            if not hasattr(Singleton, "_instance"):
                Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值