github源码指引:共享内存、数据结构与算法:平衡二叉树set带有互斥接口的

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


目录

一、演示代码

二、互斥层的实现

2.1 简单的互斥层实现

2.2 完整互斥接口的实现

2.2.1 互斥对象放在哪里

2.2.2 迭代器的互斥

2.2.3 方法的互斥

三、互斥层的设计思想


一、演示代码

        演示代码和之前的非常相似:

#include "shmSet_Mutex.h"
class CTest_T_SHMSET
{
public:
	struct DemoData : public CActiveObjectBase
	{
		typedef DemoData T_ME;
		long n = 0;
		sstring<8> s;

		//用于需要排序的场合
		bool operator < (T_ME const& tmp)const { return n < tmp.n; }
		//某些场合也需要等于
		bool operator == (T_ME const& tmp)const { return n == tmp.n; }

		friend ostream& operator << (ostream& o, T_ME const& d)
		{
			return o << d.n << " " << d.s.c_str();
		}
		//关键字的hash值,用于分块场合,应保证hash值的最后一部分仍然是平均分布的
		long keyhash()const { return n; }

		//用于输出数据的场合
		string& toString(string& str)const
		{
			char buf[2048];
			sprintf(buf, "%ld %s", n, s.c_str());
			return str = buf;
		}
		//用于表格输出
		static bool AddTableColumns(CHtmlDoc::CHtmlTable2& table)
		{
			table.AddCol("N", CHtmlDoc::CHtmlDoc_DATACLASS_RIGHT);
			table.AddCol("S", CHtmlDoc::CHtmlDoc_DATACLASS_RIGHT);
			return true;
		}
		bool AddTableData(CHtmlDoc::CHtmlTable2& table)const
		{
			table.AddData(n);
			table.AddData(s.c_str());
			return true;
		}
	};
	typedef T_SHMSET<DemoData, PI_TEST_1, CDemoData > T_CONTINER;//迭代器和数据操作都是互斥的,但连接断开等管理类操作不是互斥的(通常也不应该并发操作)
	//typedef T_SHMSET_MUTEX<DemoData, PI_TEST_1, CDemoData > T_CONTINER;//用这个也可以,但只有M开头的操作才是互斥的,定义在shmSet.h,名字有点混乱
	static int test_T_SHMSET(int argc, char** argv)
	{
		T_CONTINER a("test", 1);
		a.DestoryShm();
		if (!a.CreateShm())return __LINE__;
		thelog << endi;
		if (!a.Attach(false))return __LINE__;
		thelog << endi;
		for (int i = 0; i < 10; ++i)
		{
			DemoData tmp;
			tmp.n = rand() % 10;
			tmp.s = "abc";
			thelog << i << " n:" << tmp.n << " handle:" << a.insert(tmp).first.handle << endi;
		}
		for (T_CONTINER::const_iterator it = a.begin(); it != a.end(); ++it)
		{
			string str;
			thelog << it->toString(str) << endi;
		}
		for (int i = 0; i < 10; ++i)
		{
			DemoData tmp;
			tmp.n = i;
			T_CONTINER::const_iterator it = a.find(tmp);
			string str;
			if (it != a.end())thelog << i << " 找到 handle:" << it.handle << " " << it->toString(str) << endi;
			else thelog << i << " 没找到 handle:" << it.handle << endi;
		}
		a.RunCmdUI();

		return 0;
	}
};

        其实就是稍微改了一下,用两个typedef来简化代码编写:

typedef T_SHMSET<DemoData, PI_TEST_1, CDemoData > T_CONTINER;

        写模板经常要重复使用长长的类型定义,用typedef可以极大地简化,并减少BUG(比如漏改了一处导致的几大页的不知所云的编译报错)。不过typedef不能解决类定义里面构造函数必须和类名相同的问题。

        演示代码的编译运行和之前也没有什么区别,除了命令是22。运行输出也没什么区别。

        演示代码提示另外一个模板类也可以使用相同的测试代码,不过它们的互斥层内部实现是完全不同的。

二、互斥层的实现

2.1 简单的互斥层实现

        先看一下简单的互斥层实现,就是演示代码里注释掉的那行使用的模板类。

        基本思路是增加一个互斥对象,重新写一组接口,内部用互斥保护:

	private:
		CManagedMutex m_mutex;
	public://互斥操作,全部以M为前缀
		//清理全部数据
		bool MClear()
		{
			bool ret=true;
			if(m_mutex.WLock())
			{
				clear();
			}
			else
			{
				thelog<<GetName()<<"互斥失败: "<<m_mutex.GetErrorMessage()<<ende;
				return false;
			}

			if(!m_mutex.WUnLock())
			{
				thelog<<GetName()<<"解锁失败 : "<<m_mutex.GetErrorMessage()<<ende;
				return false;
			}
			return ret;
		}
		//报告内容,调试用
		string & MReport(string & str)const;
		//直接存取
		bool MGet(T_DATA & value)const;
		bool MUpdate(T_DATA const & value);
		bool MErase(T_DATA const & value);

        这种思路其实相当于业务层自己做互斥。好处是非常可控。

        这个模板类为什么能共享之前的测试代码呢?因为是public继承自非互斥的基础模板的。因为测试代码并没有调用M开头的带互斥的代码,所以测试代码并不是多进程、多线程安全的。

        但是为什么要public继承基础模板呢?因为有太多公共功能要用了。

        所以这里的接口设计应该是有些问题的,可以再想想。(后来我在一部分代码里面用getInterface()的方式来访问公共功能)

2.2 完整互斥接口的实现

2.2.1 互斥对象放在哪里

        因为要做完整互斥,不仅类的操作要互斥,迭代器也要互斥。迭代器如何访问互斥对象呢?

        因为与共享内存相关的互斥对象在系统内也是唯一的,所以也采用全局变量的形式在存储,用共享内存的参数PI来区分。具体实现是下面几个部分:

#define GET_PP_SET(n)				(g_shm_private_data_s[n].pSET)//获得SET对象指针


T_SHMSET * pSet=(T_SHMSET*)GET_PP_SET(PI_N);
if(pSet->m_mutex.RLock())。。。。。。

2.2.2 迭代器的互斥

        迭代器的涉及到数据的操作都需要互斥,而且整个迭代器都需要重新定义:

		struct iterator : public T_SET::iterator
		{
			using T_SET::iterator::handle;

			iterator(){handle=-1;}
			iterator(typename T_SET::iterator it){handle=it.handle;}
			iterator & operator=(typename T_SET::iterator const & it){handle=it.handle;return *this;}
			iterator & operator ++ ()
			{
				T_SHMSET * pSet=(T_SHMSET*)GET_PP_SET(PI_N);
				if(pSet->m_mutex.RLock())
				{//thelog<<"锁定"<<endi;
					iterator old=*this;
					*this=pSet->T_SET::lower_bound(**this);
					if(*this!=pSet->end())
					{
						if(**this<*old || *old<**this)
						{
						}
						else
						{
							T_SET::iterator::operator ++();
						}
					}
				}
				else
				{
					thelog<<pSet->GetName()<<"互斥失败: "<<pSet->m_mutex.GetErrorMessage()<<ende;
					handle=-1;
					return *this;
				}

				if(!pSet->m_mutex.RUnLock())
				{
					thelog<<pSet->GetName()<<"解锁失败 : "<<pSet->m_mutex.GetErrorMessage()<<ende;
					handle=-1;
					return *this;
				}//thelog<<"解锁"<<endi;
				return *this;
			}
			iterator & operator -- ()
			{
				T_SHMSET * pSet=(T_SHMSET*)GET_PP_SET(PI_N);
				if(pSet->m_mutex.RLock())
				{//thelog<<"锁定"<<endi;
					iterator old=*this;
					*this=pSet->T_SET::lower_bound(**this);
					if(*this!=pSet->end())
					{
						T_SET::iterator::operator --();
					}
				}
				else
				{
					thelog<<pSet->GetName()<<"互斥失败: "<<pSet->m_mutex.GetErrorMessage()<<ende;
					handle=-1;
					return *this;
				}

				if(!pSet->m_mutex.RUnLock())
				{
					thelog<<pSet->GetName()<<"解锁失败 : "<<pSet->m_mutex.GetErrorMessage()<<ende;
					handle=-1;
					return *this;
				}//thelog<<"解锁"<<endi;
				return *this;
			}
		};

2.2.3 方法的互斥

        所有可能造成冲突的方法都需要互斥。

        begin的互斥:

		const_iterator begin()const
		{
			const_iterator ret=end();
			T_SHM_SET_R_LOCK_BEGIN;
			ret=T_SET::begin();
			T_SHM_SET_R_LOCK_END;
		}

        用到几个宏:

#define _T_SHM_SET_R_LOCK_BEGIN 	\
			if(m_mutex.RLock())\
			{

#define _T_SHM_SET_R_LOCK_END \
			}\
			else\
			{\
				thelog<<GetName()<<"互斥失败: "<<m_mutex.GetErrorMessage()<<ende;\
				return ret;\
			}\
\
			if(!m_mutex.RUnLock())\
			{\
				thelog<<GetName()<<"解锁失败 : "<<m_mutex.GetErrorMessage()<<ende;\
				return ret;\
			}\
			return ret;

        其实跟迭代器互斥的差不多,就是为了简化代码。

        insert的互斥:

		pair<iterator, bool> insert(T_DATA const & data)
		{
			pair<iterator, bool>  ret;
			pair<typename T_SET::iterator, bool>  tmp;
			ret.first=end();
			ret.second=false;
			T_SHM_SET_W_LOCK_BEGIN;
			tmp=T_SET::insert(data);
			ret.first=tmp.first;
			ret.second=tmp.second;
			T_SHM_SET_W_LOCK_END;
		}

        用了两外两个宏,可以在源代码文件里看到。

三、互斥层的设计思想

        互斥层不论是基于业务的还是基于通用接口的,都应该严格写成一个独立的层,层内不可以互相调用(一旦互相调用,极易死锁)。

        虽然有时候可能在个特殊条件下使用非互斥层来提高速度,但是一定要明显区分互斥层和非互斥层,避免互斥层和非互斥层交替调用。


(这里是文档结束)

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值