1. 程式人生 > >c++11實現強型別快取系統

c++11實現強型別快取系統

提示:

開篇:

    看到這篇文章,或許你會有以下疑問:

    現在有各種開源的記憶體k-v快取資料庫,為啥要自己搞一個?

    幾點解釋:

        1. 這些k-v系統一般都比較重量級,當然相對於關係型資料庫可能還好,但是相對於我的這個實現來說絕對是重量級的

        2. 量級太重意味著使用會更加複雜,前期熟悉的成本高

        3. 主流的k-v系統使用場景還是類似於資料庫,提供大量資料儲存,我的實現相比之下更像是一個匕首,小而靈活,使用場景有區別

        4. 主流的k-v系統不會對語言依賴太多,所以資料型別比較少,相對於c++的強型別來說並不能無縫相容

        5. 量級重的快取系統使用c/s架構,是獨立於使用者程式以外的,在服務端使用還好,但是如果是客戶端,則基本不可用。

        6. 可以配合其他k-v或者資料庫使用,在程式記憶體中儲存少量但是熱度高的資料。

        7. 除非要落盤/持久化/轉移到其他資料儲存系統,否則在資料週轉過程中不需要對資料進行序列化和反序列化,因為資料均可以是C++直接支援的型別。

    現在提供一種使用c++實現的k-v快取系統,其中key可以是任何c++型別(但是有需要注意的細節,下面再細說),而且可以通過多個key來定位value值,並且類似於redis提供鍵的生命週期,故類名為ttl_cache

程式碼:因為是模板,故只提供了一個頭檔案

#ifndef _TTL_CACHE_H_
#define _TTL_CACHE_H_

#include <memory>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <list>
#include <map>
#include <algorithm>
#include <type_traits>
#include <typeinfo>
#include <assert.h>

namespace ttl
{
	namespace type_traits
	{
		template<typename _Tp, typename _Up>
		class _is_appendable_helper
		{
			template<typename _Tp1, typename _Up1,
				typename = decltype(std::declval<_Tp1>() += std::declval<_Up1>())>
				static std::true_type __test(int);

			template<typename, typename>
			static std::false_type __test(...);

		public:
			typedef decltype(__test<_Tp&, _Up&>(0)) type;
		};

		template<typename _Tp, typename _Up>
		struct is_appendable : public _is_appendable_helper<_Tp, _Up>::type
		{ };
	}

	using namespace std::chrono;

	enum DataStoreType	//資料暫存方式
	{
		DS_Err,		//錯誤
		DS_SINGLE,	//單條資料,更新即覆蓋
		DS_QUEUE,	//序列資料,更新即追加
	};

	typedef int DataType;

	static constexpr DataType DT_Err = -1;

	class cache_mgr;

	class cache_base
	{
	public:
		virtual ~cache_base() {}
		virtual long use_count() const noexcept
		{
			assert(false);
			return 0;
		}

	private:
		virtual void _OnCopy() {};
		virtual void _OnAppend(const cache_base& that) {};

	public:
		cache_base& operator = (const cache_base& that)
		{
			if (this != &that)
			{
				m_managed = that.m_managed;
				_OnCopy();
			}
			return *this;
		}
		cache_base& operator+=(const cache_base& that)
		{
			_OnAppend(that);
			return *this;
		}

	public:
		cache_base*	m_managed = nullptr;

		friend class cache_mgr;
	};

	class cache_mgr
	{
		enum TtlStrategy
		{
			TTL_WHEN_START,
			TTL_WHEN_ALL_RELEASE,
		};
	public:
		static cache_mgr& Instance() { static cache_mgr inst; return inst; }

	public:
		template <typename... Keys>
		bool GetCache(cache_base& _cache, DataType edt, Keys&&... keys)
		{
			typedef std::map<std::tuple<std::remove_const_t<std::remove_reference_t<Keys>>...>
				, std::weak_ptr<cache_base>> CacheMap;

			std::lock_guard<std::recursive_mutex> l(m_mutex);
			auto it1 = m_records.find(edt);
			if (it1 != m_records.end())
			{
				auto& tp = it1->second;
				CacheMap* pmap = (CacheMap*)(std::get<0>(tp));
				auto& uniqe_func = std::get<2>(tp);
				if (pmap && uniqe_func && (*uniqe_func) && (*uniqe_func)(typeid(CacheMap)))
				{
					auto it2 = pmap->find(std::forward_as_tuple(std::forward<Keys>(keys)...));
					if (it2 != pmap->end())
					{
						auto shared = it2->second.lock();
						if (shared && shared.get())
						{
							_cache = *(shared.get());
							return true;
						}
						pmap->erase(it2);
						if (pmap->empty())
						{
							delete pmap;
							m_records.erase(edt);
						}
					}
				}
			}
			return false;
		}

		template <typename... Keys>
		bool SetCache(cache_base* _cache, DataStoreType edst, DataType edt, time_t lifems, Keys&&... keys)
		{
			typedef std::map<std::tuple<std::remove_const_t<std::remove_reference_t<Keys>>...>
				, std::weak_ptr<cache_base>> CacheMap;

			std::shared_ptr<cache_base> shared(_cache);
			std::lock_guard<std::recursive_mutex> l(m_mutex);
			auto it = m_records.find(edt);
			CacheMap* pmap = nullptr;
			if (it != m_records.end())
			{
				auto& tp = it->second;
				pmap = (CacheMap*)(std::get<0>(tp));
				auto& uniqe_func = std::get<2>(tp);
				if (!(uniqe_func && (*uniqe_func) && (*uniqe_func)(typeid(CacheMap))))
					return false;
			}
			else
			{
				pmap = new CacheMap;
				struct deleter
				{
					void operator()(void* pmap)
					{
						CacheMap* ptypedmap = (CacheMap*)pmap;
						delete ptypedmap;
					}
				};
				std::function<void(void*)> *func_deleter = new std::function<void(void*)>(deleter());
				struct checker
				{
					bool operator()(const std::type_info& info)
					{
						return typeid(CacheMap) == info;
					}
				};
				std::function<bool(const std::type_info&)> *func_checker = new std::function<bool(const std::type_info&)>(checker());
				m_records[edt] = std::forward_as_tuple(pmap
					, std::unique_ptr<std::function<void(void*)>>(func_deleter)
					, std::unique_ptr<std::function<bool(const std::type_info&)>>(func_checker));
			}
			if (pmap)
			{
				auto& ptr = (*pmap)[std::forward_as_tuple(std::forward<Keys>(keys)...)];
				switch (edst)
				{
				default:
					assert(false);
					return false;
					break;
				case DS_QUEUE:
					if (ptr.expired())
						ptr = shared;
					else
					{
						*(ptr.lock().get()) += *(shared.get());
						shared.reset();
					}
					break;
				case DS_SINGLE:
					m_caches.erase(ptr.lock());
					ptr = shared;
					break;
				}
				if (shared)
					m_caches.insert(std::make_pair(std::move(shared), lifems));
				if (_CheckStrategy(TTL_WHEN_START))
					_StartTTL(_cache);
				return true;
			}
			return false;
		}

		template <typename... Keys>
		bool ClrCache(DataType edt, Keys&&... keys)
		{
			typedef std::map<std::tuple<std::remove_const_t<std::remove_reference_t<Keys>>...>
				, std::weak_ptr<cache_base>> CacheMap;

			std::lock_guard<std::recursive_mutex> l(m_mutex);
			auto it = m_records.find(edt);
			if (it != m_records.end())
			{
				auto& tp = it->second;
				CacheMap* pmap = (CacheMap*)(std::get<0>(tp));
				auto& uniqe_func = std::get<2>(tp);
				if (!(uniqe_func && (*uniqe_func) && (*uniqe_func)(typeid(CacheMap))))
					return false;
				if (pmap)
				{
					auto tp = std::forward_as_tuple(std::forward<Keys>(keys)...);
					auto itf = pmap->find(tp);
					if (itf != pmap->end())
					{
						m_caches.erase(itf->second.lock());
						pmap->erase(itf);
					}
					if (pmap->empty())
					{
						delete pmap;
						m_records.erase(edt);
					}
				}
			}
			return true;
		}

		void StartTTL(cache_base* _cache)
		{
			if (_CheckStrategy(TTL_WHEN_START))
				return;
			std::lock_guard<std::recursive_mutex> l(m_mutex);
			_StartTTL(_cache);
		}

		void StopTTL(cache_base* _cache) {/*donothing.*/ }

	private:
		void _ThreadLoop()
		{
			while (1)
			{
				std::unique_lock<std::recursive_mutex> l(m_mutex);
				m_condvar.wait_for(l, m_perchackduration);
				for (auto it = m_queue.begin(); it != m_queue.end();)
				{
					if ((!m_loop_running) || (it->first <= std::chrono::steady_clock::now()))
					{
						if ((_CheckStrategy(TTL_WHEN_ALL_RELEASE)) ? (it->second && it->second->use_count() == 1) : true)
							m_caches.erase(it->second);
						it = m_queue.erase(it);
					}
					else if ((m_loop_running) || false)
						break;
				}
				if (!m_loop_running)
					break;
			}
		}

		void _StartTTL(cache_base* _cache)
		{
			std::shared_ptr<cache_base> shared;
			auto it = std::find_if(m_caches.begin()
				, m_caches.end()
				, [&shared, _cache](std::pair<const std::shared_ptr<cache_base>, time_t>& pr)
			{
				if (pr.first.get() == _cache)
				{
					shared = pr.first;
					return true;
				}
				return false;
			});
			if (it != m_caches.end())
			{
				if (time_t(-1) != it->second)
					m_queue.insert(std::make_pair(std::chrono::steady_clock::now()
						+ std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::milliseconds(it->second))
						, std::move(shared)));
				m_condvar.notify_one();
			}
		}

		bool _CheckStrategy(TtlStrategy strategy)
		{
			return (m_strategy == TTL_WHEN_START)
				? (strategy == TTL_WHEN_START)
				: (strategy == TTL_WHEN_ALL_RELEASE);
		}

	private:
		TtlStrategy m_strategy = TTL_WHEN_START;
		std::chrono::milliseconds m_perchackduration = 5000ms;
		bool m_loop_running = true;
		std::thread* m_thread = nullptr;

		std::multimap<std::chrono::steady_clock::time_point, std::shared_ptr<cache_base>> m_queue;
		std::map<std::shared_ptr<cache_base>, time_t> m_caches;
		typedef std::tuple<void*, std::unique_ptr<std::function<void(void*)>>, std::unique_ptr<std::function<bool(const std::type_info&)>>> RecordTuple;
		typedef std::map<DataType, RecordTuple> RecordsMap;
		RecordsMap m_records;
		std::recursive_mutex m_mutex;
		std::condition_variable_any m_condvar;

	private:
		cache_mgr()
		{
			m_thread = new std::thread(std::bind(&cache_mgr::_ThreadLoop, this));
		}
		~cache_mgr()
		{
			m_loop_running = false;
			m_condvar.notify_one();
			if (m_thread)
				m_thread->join();
			delete m_thread;
			m_thread = nullptr;
			std::for_each(m_records.begin()
				, m_records.end()
				, [](std::pair<const DataType, RecordTuple>& pr)
			{
				auto& tp = pr.second;
				void* pmap = std::get<0>(tp);
				auto& uniqe_func = std::get<1>(tp);
				if (uniqe_func && *uniqe_func)
					(*uniqe_func)(pmap);
			});
		}
		cache_mgr(const cache_mgr& that) = delete;
		cache_mgr(cache_mgr&& that) = delete;
		cache_mgr& operator=(const cache_mgr& that) = delete;
		cache_mgr& operator=(cache_mgr&& that) = delete;
	};

	template <typename _Ty>
	class cache : public cache_base
	{
		typedef cache<_Ty> _Myt;

	private:
		template <typename... Args>
		void _CheckInConstructor(Args&&... _args)
		{
			if (use_count() == 1)
			{
				assert(nullptr == m_managed);
				ManageTTL(std::forward<Args>(_args)...);
			}
			else/* if (use_count() == 2)*/
			{
				assert(nullptr != m_managed);
				StopTTL();
			}
		}
		void _CheckInDestructor()
		{
			if (use_count() == 2)
			{
				StartTTL();
			}
		}
		void _OnCopy()
		{
			if (_CopyCache(this, dynamic_cast<_Myt*>(m_managed)))
			{
				_CheckInConstructor();
			}
		}
		void _OnAppend(const cache_base& that)
		{
			__OnAppend(dynamic_cast<_Myt*>(that.m_managed), type_traits::is_appendable<_Ty, _Ty>());
		}
		void __OnAppend(const _Myt* that, std::true_type&&)
		{
			if (that)
			{
				if (m_shared && that->m_shared)
				{
					*m_shared += *(that->m_shared);
				}
			}
		}
		void __OnAppend(...)
		{
			assert(false);
		}
		bool _CopyCache(cache<_Ty>* const dst, const cache<_Ty>* const src)
		{
			if (!dst || !src)
				return false;
			dst->m_shared = src->m_shared;
			dst->m_Edst = src->m_Edst;
			dst->m_Edt = src->m_Edt;
			dst->m_lifeMs = src->m_lifeMs;
			return true;
		}
		template <typename... Args>
		void _ManageTTL(Args&&... _args)
		{
			cache_mgr::Instance().SetCache(m_managed, m_Edst, m_Edt, m_lifeMs, std::forward<Args>(_args)...);
		}

	private:
		template <typename... Args>
		void ManageTTL(Args&&... _args)
		{
			m_managed = new cache<_Ty>;
			m_managed->m_managed = m_managed;
			_CopyCache(dynamic_cast<_Myt*>(m_managed), this);
			_ManageTTL(std::forward<Args>(_args)...);
		}

		void StartTTL()
		{
			cache_mgr::Instance().StartTTL(m_managed);
		}

		void StopTTL()
		{
			cache_mgr::Instance().StopTTL(m_managed);
		}

	public:
		cache() noexcept
		{	// construct empty cache
		}

		template<class _Ux,
			typename... Args>
			explicit cache(_Ux *_Px, DataStoreType _Edst, DataType _Edt, time_t _lifeMs, Args&&... _args)
			: m_shared(_Px)
			, m_Edst(_Edst)
			, m_Edt(_Edt)
			, m_lifeMs(_lifeMs)
		{	// construct cache object that owns _Px
			_CheckInConstructor(std::forward<Args>(_args)...);
		}

		template<class _Ux,
			class _Dx,
			typename... Args>
			cache(_Ux *_Px, _Dx _Dt, DataStoreType _Edst, DataType _Edt, time_t _lifeMs, Args&&... _args)
			: m_shared(_Px, _Dt)
			, m_Edst(_Edst)
			, m_Edt(_Edt)
			, m_lifeMs(_lifeMs)
		{	// construct with _Px, deleter
			_CheckInConstructor(std::forward<Args>(_args)...);
		}

		cache(std::nullptr_t) noexcept
		{	// construct empty cache
		}

		template<class _Dx,
			typename... Args>
			cache(std::nullptr_t _N, _Dx _Dt, DataStoreType _Edst, DataType _Edt, time_t _lifeMs, Args&&... _args)
			: m_shared(_N, _Dt)
			, m_Edst(_Edst)
			, m_Edt(_Edt)
			, m_lifeMs(_lifeMs)
		{	// construct with nullptr, deleter
			_CheckInConstructor(std::forward<Args>(_args)...);
		}

		template<class _Dx,
			class _Alloc,
			typename... Args>
			cache(std::nullptr_t _N, _Dx _Dt, _Alloc _Ax, DataStoreType _Edst, DataType _Edt, time_t _lifeMs, Args&&... _args)
			: m_shared(_N, _Dt, _Ax)
			, m_Edst(_Edst)
			, m_Edt(_Edt)
			, m_lifeMs(_lifeMs)
		{	// construct with nullptr, deleter, allocator
			_CheckInConstructor(std::forward<Args>(_args)...);
		}

		template<class _Ux,
			class _Dx,
			class _Alloc,
			typename... Args>
			cache(_Ux *_Px, _Dx _Dt, _Alloc _Ax, DataStoreType _Edst, DataType _Edt, time_t _lifeMs, Args&&... _args)
			: m_shared(_Px, _Dt, _Ax)
			, m_Edst(_Edst)
			, m_Edt(_Edt)
			, m_lifeMs(_lifeMs)
		{	// construct with _Px, deleter, allocator
			_CheckInConstructor(std::forward<Args>(_args)...);
		}

		template<class _Ty2>
		cache(const cache<_Ty2>& _Right, _Ty *_Px) noexcept : m_shared(_Right.m_shared, _Px)
		{	// construct cache object that aliases _Right
			m_managed = _Right.m_managed;
			m_Edst = _Right.m_Edst;
			m_Edt = _Right.m_Edt;
			m_lifeMs = _Right.m_lifeMs;
			_CheckInConstructor();
		}

		cache(const _Myt& _Other) noexcept : m_shared(_Other.m_shared)
		{	// construct cache object that owns same resource as _Other
			m_managed = _Other.m_managed;
			m_Edst = _Other.m_Edst;
			m_Edt = _Other.m_Edt;
			m_lifeMs = _Other.m_lifeMs;
			_CheckInConstructor();
		}

		template<class _Ty2,
			class = typename std::enable_if<std::is_convertible<_Ty2 *, _Ty *>::value,
			void>::type>
			cache(const cache<_Ty2>& _Other) noexcept : m_shared(_Other.m_shared)
		{	// construct cache object that owns same resource as _Other
			m_managed = _Other.m_managed;
			m_Edst = _Other.m_Edst;
			m_Edt = _Other.m_Edt;
			m_lifeMs = _Other.m_lifeMs;
			_CheckInConstructor();
		}
		_Myt& operator=(_Myt&& _Right) noexcept
		{	// take resource from _Right
			cache(std::move(_Right)).swap(*this);
			return (*this);
		}

		template<class _Ty2>
		_Myt& operator=(cache<_Ty2>&& _Right) noexcept
		{	// take resource from _Right
			cache(std::move(_Right)).swap(*this);
			return (*this);
		}

		_Myt& operator=(const _Myt& _Right) noexcept
		{	// assign shared ownership of resource owned by _Right
			cache(_Right).swap(*this);
			return (*this);
		}

		template<class _Ty2>
		_Myt& operator=(const cache<_Ty2>& _Right) noexcept
		{	// assign shared ownership of resource owned by _Right
			cache(_Right).swap(*this);
			return (*this);
		}

		cache(_Myt&& _Right) noexcept
			: cache_base(std::move(_Right))
			, m_shared(std::move(_Right.m_shared))
			, m_Edst(std::move(_Right.m_Edst))
			, m_Edt(std::move(_Right.m_Edt))
			, m_lifeMs(std::move(_Right.m_lifeMs))
		{	// construct cache object that takes resource from _Right
		}

		void swap(_Myt& _Other) noexcept
		{	// swap pointers
			m_shared.swap(_Other.m_shared);
			std::swap(m_managed, _Other.m_managed);
			std::swap(m_Edst, _Other.m_Edst);
			std::swap(m_Edt, _Other.m_Edt);
			std::swap(m_lifeMs, _Other.m_lifeMs);
		}

		_Ty *get() const noexcept
		{	// return pointer to resource
			return (m_shared.get());
		}

		long use_count() const noexcept
		{	// return use count
			return m_shared.use_count();
		}

		typename std::add_lvalue_reference<_Ty>::type operator*() const noexcept
		{	// return reference to resource
			return (*(m_shared.get()));
		}

		_Ty *operator->() const noexcept
		{	// return pointer to resource
			return (m_shared.get());
		}

		~cache() noexcept
		{	// release resource
			_CheckInDestructor();
		}

	private:
		std::shared_ptr<_Ty> m_shared;
		DataStoreType m_Edst = DS_Err;
		DataType m_Edt = DT_Err;
		time_t m_lifeMs = 60000;
	};
}

template<class _Ty>
void swap(ttl::cache<_Ty>& _Left,
	ttl::cache<_Ty>& _Right) noexcept
{	// swap _Left and _Right shared_ptrs
	_Left.swap(_Right);
}

#endif // _TTL_CACHE_H_

程式碼基於標準c++11,所以理論上不存在平臺限制(目前已經驗證能在"g++7.2.0","ms-vs2015"和"clang-900.0.38"編譯通過)。

使用場景:

    時序圖如下:

    上面的ttl_cache系統就是圖中的"cache layer"。

存在的問題:

    1. “ttl_cache_mgr”的介面都是用了泛型引數,並且內部為了相容不同資料型別使用了void*指標強轉,所以在程式設計上面容易引起編譯期發現不了但是執行期可能導致崩潰的問題(記憶體損壞問題已經解決,請參照最新程式碼),但是理論上來說只要嚴格遵照自己設計的資料格式來對應Set和Get的引數,是不會出現上述問題的。此問題可以通過再次包裝,通過模板的編譯期分派,提供更高一層的確定引數的Get和Set版本,從而在編譯期就能夠發現引數不對應問題。相關程式碼我會在後續補上對SetCache的改動涉及到程式碼結構的調整,暫時不提供程式碼,具體原因請參考github中todolist)。

    2. 同樣由於使用了void*,如果在快取記錄結構析構時快取沒有全部消費掉,清理map時呼叫delete(void*)會有問題,因為無法得到型別資訊,delete並不會呼叫解構函式已解決)。

    3. 對使用該快取系統的資料型別的要求:

        1) 作為Set和Get操作中“keys“的引數型別都需要能夠比較,即operator<的實現

        2) 指定了暫存型別為“DS_QUEUE”的資料結構需要支援operator+=操作(再次感謝std開源,參照std::is_assignable實現了ttl_cache::type_traits::is_appendable)

    4. 目前只是從原理上初步實現並驗證了此快取系統的可行性,但是在目前的第一個版本中肯定還存在各種問題,諸如一些細節上的實現技巧是否有更好的方式,演算法和資料結構選擇是否合理,內部處理策略是否可以再優化,是否應該選用合適的記憶體池來組織快取資料等等。還希望有大神能夠不吝賜教

使用例程:(以下測試例程只為簡單說明使用方法,所以均使用單一執行緒,但實際使用中建立快取和使用快取一般不會出現在同一位置,可能是不同的執行緒之間,需要注意)

#include "ttl_cache.h"

#include <iostream>
#include <string>

using namespace std::chrono;
using namespace std::string_literals;

enum EmDataType : ttl::DataType	//資料型別
{
	DT_Err = ttl::DT_Err,	//錯誤
	DT_AccountInfo,
	DT_AccountList,
	DT_TradeHistory,
};

class AccountInfo
{
public:
	AccountInfo(long id, const std::string& owner, const std::string& bank, unsigned long balance)
		: m_id(id)
		, m_owner(owner)
		, m_bank(bank)
		, m_balance(balance)
	{
		std::cout << "AccountInfo standard constructor." << std::endl;
	}
	AccountInfo(const AccountInfo& right)
	{
		m_id = (right.m_id);
		m_owner = (right.m_owner);
		m_bank = (right.m_bank);
		m_balance = (right.m_balance);
		std::cout << "AccountInfo copy constructor." << std::endl;
	}
	AccountInfo(AccountInfo&& right)
	{
		m_id = std::move(right.m_id);
		m_owner = std::move(right.m_owner);
		m_bank = std::move(right.m_bank);
		m_balance = std::move(right.m_balance);
		std::cout << "AccountInfo move constructor." << std::endl;
	}
	~AccountInfo()
	{
		std::cout << "AccountInfo destructor." << std::endl;
	}

public:
	long m_id;
	std::string m_owner;
	std::string m_bank;
	unsigned long m_balance;
};

//使用場景下,不需要保持right不變(const),so,使用此種寫法效能更高
std::list<AccountInfo>& operator+=(std::list<AccountInfo>& left, std::list<AccountInfo>& right)
{
	for(auto it = right.begin(); it != right.end(); ++it)
		left.push_back(std::move(*it));
	return left;
}

class TradeHistory
{
public:
	TradeHistory(long account)
		: m_account(account)
	{
		std::cout << "TradeHistory standard constructor." << std::endl;
	}
	TradeHistory(const TradeHistory& right)
	{
		m_account = (right.m_account);
		m_history = (right.m_history);
		std::cout << "TradeHistory copy constructor." << std::endl;
	}
	TradeHistory(TradeHistory&& right)
	{
		m_account = std::move(right.m_account);
		m_history = std::move(right.m_history);
		std::cout << "TradeHistory move constructor." << std::endl;
	}
	~TradeHistory()
	{
		std::cout << "TradeHistory destructor." << std::endl;
	}
	TradeHistory& operator+=(TradeHistory& that)
	{
		if (m_account == that.m_account)
			for (auto it = that.m_history.begin(); it != that.m_history.end(); ++it)
				m_history.push_back(std::move(*it));
		return *this;
	}

public:
	long m_account;
	std::list<std::pair<long, std::string>> m_history;	//list<收支金額(+收入/-支出),金額變動說明>
};

void testttlcache_1()//單條資料
{
	//1. 沒有快取時獲取失敗
	ttl::cache<AccountInfo> s1;
	//約定後兩個引數表示取【持有人為"zxj"的賬號為"100101"的賬戶】的快取資料
	bool ret1 = ttl::cache_mgr::Instance().GetCache(s1, DT_AccountInfo, "zxj"s, 100101);
	//2.1) 增加一個生命週期10s的快取資料
	{
		ttl::cache<AccountInfo> c1(new AccountInfo(100101, "zxj", "中國銀行", 666666), ttl::DS_SINGLE, DT_AccountInfo, 10000, "zxj"s, 100101);
	}
	//2.2) 即時離開了c1的作用域,一樣能夠獲取到前面加入的快取資料,【注意:後兩個引數型別與增加快取時對應】
	ttl::cache<AccountInfo> s2;
	bool ret2 = ttl::cache_mgr::Instance().GetCache(s2, DT_AccountInfo, "zxj"s, 100101);
	//3.1) 無法獲取到未加入的快取資料,【注意:後兩個引數型別與增加快取時對應】
	ttl::cache<AccountInfo> s3;
	bool ret3 = ttl::cache_mgr::Instance().GetCache(s3, DT_AccountInfo, "lxy"s, 100102);
	//3.2) 增加一個生命週期10s的快取資料
	ttl::cache<AccountInfo> c2(new AccountInfo(100102, "lxy", "建設銀行", 654321), ttl::DS_SINGLE, DT_AccountInfo, 10000, "lxy"s, 100102);
	//3.3) 這時可以獲取到(對比3.1步驟)
	ttl::cache<AccountInfo> s4;
	bool ret4 = ttl::cache_mgr::Instance().GetCache(s4, DT_AccountInfo, "lxy"s, 100102);
	//4.1) 修改了快取中的賬戶餘額
	ttl::cache<AccountInfo> c3(new AccountInfo(100101, "zxj", "中國銀行", 999999), ttl::DS_SINGLE, DT_AccountInfo, 10000, "zxj"s, 100101);
	//4.2) 重新獲取快取資料,得到最新資料
	ttl::cache<AccountInfo> s5;
	bool ret5 = ttl::cache_mgr::Instance().GetCache(s5, DT_AccountInfo, "zxj"s, 100101);
	//5.1) 清除【持有人為"zxj"的賬號為"100101"的賬戶】的快取資料
	ttl::cache_mgr::Instance().ClrCache(DT_AccountInfo, "zxj"s, 100101);
	//5.2) 獲取不到對應的快取了
	ttl::cache<AccountInfo> s6;
	bool ret6 = ttl::cache_mgr::Instance().GetCache(s6, DT_AccountInfo, "zxj"s, 100101);
	//6. 使用和最開始呼叫不同的keys引數建立新快取,會導致記憶體混亂,或致崩潰
	ttl::cache<AccountInfo> c4(new AccountInfo(100101, "zxj", "中國銀行", 888888), ttl::DS_SINGLE, DT_AccountInfo, 10000, "zxj"s, 100101,"中國銀行"s);
	ttl::cache<AccountInfo> s7;
	bool ret7 = ttl::cache_mgr::Instance().GetCache(s7, DT_AccountInfo, "zxj"s, 100101, "中國銀行"s);
	//7. 等到快取生命週期結束,則不能再獲取到快取資料【注意:具體能不能獲取到和ttl::ttl_cache_mgr的快取策略"ttl::ttl_cache_mgr::TtlStrategy"有關】
	std::this_thread::sleep_for(std::chrono::seconds(10));
	ttl::cache<AccountInfo> s8;
	bool ret8 = ttl::cache_mgr::Instance().GetCache(s8, DT_AccountInfo, "lxy"s, 100102);
}

void testttlcache_2()
{
	//1. 沒有快取時獲取失敗
	ttl::cache<std::list<AccountInfo>> s1;
	// keys只有一個引數,表示獲取【持有人為"zxj"的賬戶列表】快取資料
	bool ret1 = ttl::cache_mgr::Instance().GetCache(s1, DT_AccountList,"zxj"s);
	//2.1) 增加一個快取資料,序列資料涉及到實時更新,比如訂閱推送資料,可指定生命週期為無限長(time_t(-1))
	auto l1 = new std::list<AccountInfo>;
	l1->emplace_back(100103, "zxj", "交通銀行", 900000);
	ttl::cache<std::list<AccountInfo>> c1(l1, ttl::DS_QUEUE, DT_AccountList, -1, "zxj"s);
	//2.2) 獲取前面加入的快取資料
	ttl::cache<std::list<AccountInfo>> s2;
	bool ret2 = ttl::cache_mgr::Instance().GetCache(s2, DT_AccountList, "zxj"s);
	//3.1) 增加兩個快取資料項
	auto l2 = new std::list<AccountInfo>;
	l2->emplace_back(100104, "zxj", "農業銀行", 980000);
	l2->emplace_back(100105, "zxj", "工商銀行", 990000);
	ttl::cache<std::list<AccountInfo>> c2(l2, ttl::DS_QUEUE, DT_AccountList, -1, "zxj"s);
	//3.2) 獲取前面加入的快取資料,應該有三條資料
	ttl::cache<std::list<AccountInfo>> s3;
	bool ret3 = ttl::cache_mgr::Instance().GetCache(s3, DT_AccountList, "zxj"s);
	//4. 清除前面加入的"zxj"名下的快取
	ttl::cache_mgr::Instance().ClrCache(DT_AccountList,"zxj"s);
	//5.1) 增加一個新的快取資料項
	auto l3 = new std::list<AccountInfo>;
	l3->emplace_back(100106, "zxj", "招商銀行", 970000);
	ttl::cache<std::list<AccountInfo>> c3(l3, ttl::DS_QUEUE, DT_AccountList, -1, "zxj"s);
	//2.2) 獲取前面加入的快取資料,應該只有一條資料
	ttl::cache<std::list<AccountInfo>> s4;
	bool ret4 = ttl::cache_mgr::Instance().GetCache(s4, DT_AccountList, "zxj"s);
}

void testttlcache_3()
{
	//1. 沒有快取時獲取失敗
	ttl::cache<TradeHistory> s1;
	// keys只有一個引數,表示獲取【賬號為"100101"的賬戶的交易明細】快取資料
	bool ret1 = ttl::cache_mgr::Instance().GetCache(s1, DT_TradeHistory, 100101);
	//2.1) 增加一個快取資料,序列資料涉及到實時更新,比如訂閱推送資料,可指定生命週期為無限長(time_t(-1))
	auto t1 = new TradeHistory(100101);
	t1->m_history.emplace_back(6666, "發工資啦");
	t1->m_history.emplace_back(-1000, "樓下足療店一擲千金");
	ttl::cache<TradeHistory> c1(t1, ttl::DS_QUEUE, DT_TradeHistory, -1, 100101);
	//2.2) 獲取前面加入的快取資料
	ttl::cache<TradeHistory> s2;
	bool ret2 = ttl::cache_mgr::Instance().GetCache(s2, DT_TradeHistory, 100101);
	//3.1) 增加兩個快取資料項
	auto t2 = new TradeHistory(100101);
	t2->m_history.emplace_back(100000104, "突然多了這麼多,銀行系統bug了?");
	t2->m_history.emplace_back(-100000000, "XX銀行:sorry~,上一筆系清潔工手誤。。");
	ttl::cache<TradeHistory> c2(t2, ttl::DS_QUEUE, DT_TradeHistory, -1, 100101);
	//3.2) 獲取前面加入的快取資料,應該有四條資料
	ttl::cache<TradeHistory> s3;
	bool ret3 = ttl::cache_mgr::Instance().GetCache(s3, DT_TradeHistory, 100101);
	//4. 清除前面加入的"100101賬戶"名下的快取
	ttl::cache_mgr::Instance().ClrCache(DT_TradeHistory, 100101);
	//5.1) 增加一個新的快取資料項
	auto t3 = new TradeHistory(100101);
	t3->m_history.emplace_back(-1314520, "emmm,光棍節發紅包支出");
	ttl::cache<TradeHistory> c3(t3, ttl::DS_QUEUE, DT_TradeHistory, -1, 100101);
	//2.2) 獲取前面加入的快取資料,應該只有一條資料
	ttl::cache<TradeHistory> s4;
	bool ret4 = ttl::cache_mgr::Instance().GetCache(s4, DT_TradeHistory, 100101);
}

void test_ttlcache()
{
	testttlcache_1();
	testttlcache_2();
	testttlcache_3();
}

int main()
{
	test_ttlcache();
	return 1;
}

測試例程中使用了針對std::string的operator""s(c14)操作符,所以g++或clang編譯請加上-std=c++14選項

相關推薦

c++11實現型別快取系統

提示: 開篇:     看到這篇文章,或許你會有以下疑問:     現在有各種開源的記憶體k-v快取資料庫,為啥要自己搞一個?     幾點解釋:         1. 這些k-v系統一般都比較重量級,當然相對於關係型資料庫可能還好,但是相對於我的這個實現來說絕對是重

C++11型別列舉(enum)

// C++11之前的enum型別是繼承C的,不溫不火; // C++11對enum動刀了,加強了型別檢查,推出強型別enum型別,眼前一亮 // 使用過QT 的都知道,早就應該這麼做了,用的很爽!! // 一、C中enum型別的侷限 // 1、非強型別作用域 enum

C++11 實現信號量Semaphore類

signed clas 可能 details 時有 art one http spa 1 #pragma once 2 #include <mutex> 3 #include <condition_variable> 4 class Sem

(Google面試題)有四個線程1、2、3、4同步寫入數據……C++11實現

blog image more http auto 最終 進行 .get fall 最近在學習多線程,題目源自 MoreWindows先生的 《秒殺多線程第一篇》(http://blog.csdn.net/morewindows/article/details/739274

C++11實現一個簡單的線程池

start art AI fun con var func iostream any 為了不讓手生,邊復習邊手擼了一個線程池,代碼量比較少,如下,用了一些C++11的實現,語言標準嘛,就是跨平臺的: thread_poo.h #ifndef _THREAD_POOL_ #

C++11實現一個有界的阻塞隊列

ide true 多線程編程 from ces locker sid const read 對於一個無界的阻塞隊列而言,其實現非常簡單,即用一個鎖(鎖隊列)+ 一個條件變量(判空)即可。那麽對於一個有界阻塞隊列而言,其隊列的容量有上限,其實只要再加一個條件變量用來判斷是否滿

C++11實現生產者和消費者

#include <iostream> #include <thread> #include <mutex> #include <deque> #include <vector> #include <condition

C++11實現一個自動註冊的工廠

轉自:https://www.cnblogs.com/qicosmos/p/5090159.html 實現動機        工廠方法是最簡單地建立派生類物件的方法,也是很常用的,工廠方法內部使用switch-

C++11實現訊號量

由於C++11 和 Boost.Thread 都沒有提供訊號量,但是對於這個簡單的東西,有時候使用就是太簡單,但是為什麼沒有,可能他們覺得是這個東西太容易出錯了,所以自己實現也不是很複雜。 直接上程式碼: #include <condition_variable> #inc

基於C++11實現執行緒池的工作原理.

基於C++11實現執行緒池的工作原理. 文章目錄 基於C++11實現執行緒池的工作原理. 簡介 執行緒池的組成 1、執行緒池管理器 2、工作執行緒 3、任務介面, 4、任務佇列

C++11實現的定時器

分享一個基於C++11實現的定時器,當有多個定時任務時,向定時器裡面新增定時任務,定時器到時間自動執行事件,編譯環境(GCC) 4.7.2 ,參考程式碼 Timer.h #ifndef _X_TIMER_H #define _X_TIMER_H #include <map>

基於C++11實現執行緒池的工作原理

基於C++11實現執行緒池的工作原理. 不久前寫過一篇執行緒池,那時候剛用C++寫東西不久,很多C++標準庫裡面的東西沒怎麼用,今天基於C++11重新實現了一個執行緒池。 簡介 執行緒池(thread pool):一種執行緒的使用模式,執行緒過多會帶來排程開銷,進而影響快取區域性性和整體效能。而執行緒池

利用C++11實現執行緒task的簡單封裝

#include <functional> #include <thread> #include <type_traits> /*Compile only if 'F' is callable. F maybe function, la

C++11的POD型別

POD型別的定義 必須是平凡的和有標準佈局的。 平凡的建構函式與解構函式是預設的,不起任何作用的。如果自定義了,那麼需要宣告為default型別的。 使用std::is_trivial進行判別。 標準佈

[原始碼和文件分享]基於C語言實現的網咖管理系統-背單詞-自守數-進位制轉換

1 求解自守數 1.1 問題描述 判斷任意輸入的某數,是否是自守數。如果一個自然數的平方數的尾部仍然為該自然數本身, 則稱其為自守數。例如: 5x5=25 76x76=5776 625x625=390625 1.2 功能要求 可任意輸入一個整數,輸出其是否是

併發程式設計(三): 使用C++11實現無鎖stack(lock-free stack)

C++11中CAS實現: template< class T> struct atomic { public: bool compare_exchange_weak( T& expected, T desired,                    

C++11:自動型別推導與型別獲取

auto 話說C語言還處於K&R時代,也有auto a = 1;的寫法。中文譯過來叫自動變數,跟c++11的不同,C語言的auto a = 1;相當與 auto int a = 1;語句。 而C++11的auto是有著嚴格的型別推匯出來的。以前是這麼寫

C++11 | 執行時型別識別(RTTI)

type_info類 typeid操作符 type_index類 type_info type_info類在標頭檔案<typeinfo>中定義,代表了一個C++型別的相關資訊。一般由t

C++11 Auto自動型別

// auto.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。 // #include "pch.h" #include <iostream> #include <map> #include <unordered_map> using

[原始碼和文件分享]基於C語言實現的考試報名系統

1 專案簡介 考試報名工作給各高校報名工作帶來了新的挑戰,給教務管理部門增加了很大的工作量。本專案是對考試報名管理的簡單模擬,用控制檯選項的選擇方式完成下列功能:輸入考生資訊;輸出考生資訊;查詢考生資訊;新增考生資訊;修改考生資訊;刪除考生資訊。 2 專案功能要求 本專案的實質是完成對考