程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)2

        承接上文:程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)-CSDN博客

        完整源码在上文已经给出。本文解释其中的主要代码。以下代码删除了一些辅助功能。

目录

一、核心数据

二、创建信号量

三、销毁信号量

四、连接到信号量

五、获取信号量创建时间

 六、读锁定

七、写锁定

八、释放读锁定

九、释放写锁定


一、核心数据

		int sem_id{ -1 };//信号量ID

        一切都围绕这个进行。sem_id是系统的信号量的标识,所有信号量操作都针对这个。

        这个值通过创建(semget)获得,然后把获得的ID存储在合适的位置,其它程序可以通过ID操作这个信号量。

二、创建信号量

		//创建新信号量
		bool Create()
		{

#ifdef _LINUXOS
			sem_id = semget(IPC_PRIVATE, 4, IPC_CREAT | IPC_EXCL | 0666);
#else
			sem_id = semget(IPC_PRIVATE, 4, IPC_CREAT | IPC_EXCL | SEM_R | (SEM_R >> 3) | (SEM_R >> 6) | SEM_A | (SEM_A >> 3) | (SEM_A >> 6));
#endif
			if (-1 == sem_id)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			union semun sem;
			sem.val = 1;
			if (semctl(sem_id, 0, SETVAL, sem) < 0)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			sem.val = 0;
			if (semctl(sem_id, 1, SETVAL, sem) < 0)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			sem.val = 0;
			if (semctl(sem_id, 2, SETVAL, sem) < 0)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			sem.val = 0;
			if (semctl(sem_id, 3, SETVAL, sem) < 0)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			return true;
		}

        这个代码首先创建一个信号量,包含四个值。第一个参数key为IPC_PRIVATE,表示由系统分配。由于信号量是全系统范围的,因此必须保证在全系统范围内没有冲突。对于socket端口,由国际组织分配服务端口号,而对于信号量,并没有什么国际组织管理这个,所以我总是使用系统分配。还有些老代码喜欢用磁盘路径创建一个唯一值作为key,但是如果路径被删除重建,key就会改变,从而导致互斥失败。所以归根结底,没有靠谱的方法。

        创建信号量之后就要初始化每个值,然后才能使用。

三、销毁信号量

        一般情况下创建了就不用删除了,除非发生了BUG。

		//删除信号量
		bool Destory()
		{

			union semun sem;
			sem.val = 0;
			if (semctl(sem_id, 0, IPC_RMID, sem) < 0)
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				return false;
			}
			else
			{
				sem_id = -1;
				return true;
			}
		}

四、连接到信号量

        这个功能只是设置这个类要操作的sem_id而已。作为一个实用代码,做了一些检查,以确认这个信号量是自己系统的信号量,而不是别的系统的。

		bool Attach(int id, time_t const& ctime)
		{
			sem_id = id;

			//检查
			time_t tmp_ctime;
			if (!_GetCTime(tmp_ctime))
			{
				m_errid = __LINE__;
				m_errmsg.str("");
				m_errmsg << errno2str();
				sem_id = -1;
				return false;
			}
			else
			{
				if (tmp_ctime != ctime)
				{
					m_errid = __LINE__;
					m_errmsg.str("");
					m_errmsg << "ctime不匹配";
					sem_id = -1;
					return false;
				}
			}

			return -1 != sem_id;
		}

        具体检查方式就是检查信号量的创建时间和预期是否一致。信号量创建时间和ID是一起保存的。

五、获取信号量创建时间

        创建时间可以用来确认信号量确实是自己的。

		bool _GetCTime(time_t& ctime)const
		{
			semid_ds ds;
			union semun sem;
			int ret;

			memset(&ds, 0, sizeof(semid_ds));
			sem.buf = &ds;
			if ((ret = semctl(sem_id, 0, IPC_STAT, sem)) < 0)
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << "获得sem " << sem_id << " 状态出错(" << ret << "):" << errno2str();
				return false;
			}
			ctime = ds.sem_ctime;
			return true;
		}

 六、读锁定

		bool _RLock(bool no_wait)const
		{
			constexpr short int flag = SEM_UNDO;
			constexpr short int flag_nowait = SEM_UNDO | IPC_NOWAIT;

			struct sembuf sops_wait[] = { {1,0,flag},{2,1,flag},{3,0,flag} };
			struct sembuf sops_nowait[] = { {1,0,flag_nowait},{2,1,flag_nowait},{3,0,flag_nowait} };

			struct sembuf* sops = sops_wait;
			if (no_wait)sops = sops_nowait;

			if (-1 != semop(sem_id, sops, 3))
			{
				after_RLock();
				return true;
			}
			else
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << errno2str();
				return false;
			}
		}

七、写锁定

		bool _WLock(bool no_wait)const
		{
			STATIC_C short int const flag = SEM_UNDO;
			STATIC_C short int const flag_nowait = SEM_UNDO | IPC_NOWAIT;

			struct sembuf sops_wait[] = { {0,-1,flag},{1,1,flag},{2,0,flag},{3,1,flag} };
			struct sembuf sops_nowait[] = { {0,-1,flag_nowait},{1,1,flag_nowait},{2,0,flag_nowait},{3,1,flag_nowait},{3,-1,flag_nowait} };

			struct sembuf* sops = sops_wait;
			if (no_wait)sops = sops_nowait;

			//先增加写等待数,阻止读请求被激活
			if (-1 == semop(sem_id, &sops[3], 1))
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << errno2str();
				return false;
			}
			if (-1 != semop(sem_id, sops, 3))
			{
				after_WLock();
				return true;
			}
			else if (no_wait && EAGAIN == errno)
			{//撤销写等待
				if (-1 == semop(sem_id, &sops[4], 1))
				{
					*(int*)&m_errid = __LINE__;
					(*(stringstream*)&m_errmsg).str("");
					*(stringstream*)&m_errmsg << errno2str();
				}
				return false;
			}
			else
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << errno2str();
				return false;
			}
		}

八、释放读锁定

		bool RUnLock()const
		{
			struct sembuf sops[] = { {2,-1,SEM_UNDO} };
			if (-1 != semop(sem_id, sops, 1))
			{
				after_RUnLock();
				return true;
			}
			else
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << errno2str();
				return false;
			}
		}

九、释放写锁定

		bool WUnLock()const
		{
			struct sembuf sops[] = { {0,1,SEM_UNDO},{1,-1,SEM_UNDO},{3,-1,SEM_UNDO} };
			if (-1 != semop(sem_id, sops, 3))
			{
				after_WUnLock();
				return true;
			}
			else
			{
				*(int*)&m_errid = __LINE__;
				(*(stringstream*)&m_errmsg).str("");
				*(stringstream*)&m_errmsg << errno2str();
				return false;
			}
		}

(这里是结束)

你可能感兴趣的:(程序员造轮子(我的可重用代码),信号量,读写锁,写优先)