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

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

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

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


        承接上文:程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)-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;
		}

 六、读锁定

        操作分两种:阻塞或不阻塞。等等……代码好像删多了,没有处理EAGAIN,不能区分出错返回和锁定失败。

		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;
			}
		}

七、写锁定

        也有两种方式:阻塞和不阻塞。不阻塞时semop返回的是EAGAIN,可以根据这个重试。

		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;
			}
		}

九、释放写锁定

        写资源+1,写计数-1,写等待-1。逻辑上在写锁定的同时应该做写等待-1的,不过没有实质区别。

		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;
			}
		}

(这里是结束)

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初级代码游戏

知识究竟是有价还是无价

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值