多线程的互斥锁应用RAII机制

什么是RAII机制

RAII是Resource Acquisition Is Initialization(翻译成 “资源获取即初始化”)的简称,是C++语言的一种管理资源、避免资源泄漏的惯用法,该方法依赖构造函数资和析构函数的执行机制。

RAII的做法是使用一个类对象,在对象的构造函数中获取资源,在对象生命期内控制对资源的访问,最后在对象消失时,其析构函数来释放获取的资源;

这里的资源可以是文件句柄,内存,Event,互斥量等等,由于系统的资源是有限的,就好比自然界的石油,铁矿一样,不是取之不尽,用之不竭的。所以,我们在编程安全上,要求必须遵循以下几个步骤:

    申请资源
    使用资源
    释放资源

在步骤一和步骤二上,我们平时都比较容易把握,而资源的释放会因为种种编码原因容易被忽略,导致系统资源实际没有使用了,但却没有释放或者引发其他问题,影响了系统资源利用率。
没有使用RAII机制的弊端

那么我们为什么涉及资源管理时,建议使用RAII机制进行编码呢?

大家可以查看以下两篇文章中的代码,我在这两篇文章中都是直接使用系统API进行资源操作,没有使用RAII机制进行资源管理,在代码阅读和维护上都很不方便。

多线程死锁的产生和解决
利用关键代码段实现线程同步

不推荐的编码方式片段:

while (TRUE){//等待直到获得指定对象的所有权EnterCriticalSection(&g_csLock); //关键代码段-beginif (g_nIndex++ < nMaxCnt){cout << "Index = "<< g_nIndex << " ";cout << "Thread2 is runing" << endl;//权限释放,容易忘记LeaveCriticalSection(&g_csLock);}else{//权限释放,容易忘记LeaveCriticalSection(&g_csLock);//关键代码段-endbreak;} }

之所以不推荐这样的编码方式是因为EnterCriticalSection/LeaveCriticalSection必须配对使用,很需要依赖人,无法根本上解决问题,如果LeaveCriticalSection函数没有执行或者忘记添加该API很容易引发问题。
互斥锁应用RAII机制

为了从根本上解决问题,减少人为因素引发应用系统问题或者资源泄漏,在关键代码段和互斥量这两种锁上示范了如何应用RAII机制,简化多线程互斥编码。

关键代码段初始化和锁接口:

class CSLock
{
public:CSLock(){//构造函数时初始化关键代码段对象,获取资源InitializeCriticalSection(&m_csLock);}~CSLock(){//析构函数时释放为关键代码段对象分配的所有资源,释放资源DeleteCriticalSection(&m_csLock);}//生命周期内实现对象资源的管理(Lock/Unlock),使用资源void Lock(){EnterCriticalSection(&m_csLock);}void Unlock(){LeaveCriticalSection(&m_csLock);}//阻止锁的拷贝和赋值
private:CSLock (const CSLock& );CSLock& operator  = (const CSLock&);
private:CRITICAL_SECTION m_csLock;
};

创建互斥量对象和锁接口:

class CMutexLock
{
public:CMutexLock(){m_hMutex = CreateMutex(NULL, FALSE, NULL);//获取资源}~CMutexLock(){CloseHandle(m_hMutex);//释放资源}void Lock(){WaitForSingleObject(m_hMutex, INFINITE);//使用资源}void Unlock(){ReleaseMutex(m_hMutex);//使用资源}//阻止锁的拷贝和赋值
private:CMutexLock(const CMutexLock&);CMutexLock& operator= (const CMutexLock&);
private:HANDLE  m_hMutex;
};

类模板对象,再一次使用RAII机制管理锁对象的占用和释放,建议简化锁的应用,实现资源的自动回收

template<class T>
class CLockGuard
{
public:CLockGuard(T& locker) :m_lockerObj(locker){m_lockerObj.Lock();}~CLockGuard(){m_lockerObj.Unlock();}
private:T& m_lockerObj; //必须是引用类型 确保使用的是全局锁,否则锁不住
};

具体示例:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <Windows.h>//创建全局锁,保证锁就一个
CSLock      g_csLock;
CMutexLock  g_Mutex;//全局数据
int         g_nIndex = 0;
const int   nMaxCnt  = 30;BOOL AddNum(int tid)
{BOOL bRet = TRUE;//RAII用法,创建lock对象的同时执行lock操作,析构后自动调用unlock操作,避免人为遗漏CLockGuard<CMutexLock> lock(g_Mutex);if (g_nIndex++ < nMaxCnt){std::cout << "Index = " << g_nIndex << " ";std::cout << "thread " << tid << " is runing" << std::endl;}else{bRet = FALSE;}return bRet;
}//线程函数1
DWORD WINAPI Thread1(LPVOID lpParameter)
{while (true){if (!AddNum(1)){break;}}return 0;
}
//线程函数2
DWORD WINAPI Thread2(LPVOID lpParameter)
{while (true){if (!AddNum(2)){break;}}return 0;
}int _tmain(int argc, _TCHAR* argv[])
{HANDLE harThread[2] = {NULL,NULL};//创建新的线程harThread[0] = CreateThread(NULL, 0, Thread1, NULL, 0, NULL);//立即执行harThread[1] = CreateThread(NULL, 0, Thread2, NULL, 0, NULL);//立即执行WaitForMultipleObjects(2, harThread, TRUE, INFINITE);//良好的编码习惯for (int i = 0; i < 2; i++){CloseHandle(harThread[i]);}return 0;
}

运行效果:


从输出结果上看,我们的锁是生效的,没有出现错乱。这里使用了CLockGuard模板类来进一步简化多线程锁的编码,既实现了代码复用也保证了编码安全。其实,这编码方式在C++11中lock_guard已经应用到了该机制。点击这里查看lock_guard(lock_guard - C++ Reference)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

asp.net 浏览服务器文件

http://www.csharpwin.com/dotnetspace/12018r482.shtml 前台文件file.aspx <% Page Language"C#"AutoEventWireup"true"CodeFile"file.aspx.cs"Inherits"file"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transi…

使用jQuery queue(队列) 遇到的问题及解决方案

应用场景描述&#xff1a; 我现在要做文章列表的批量生成&#xff0c;使用AJAX将生成的进度情况展示给用户。首先要生成文章列表页&#xff0c;然后在生文章内容详细页。 假如有10页每页10条记录&#xff0c;就会10个文章列表页 总录数&#xff08;100条记录&#xff09; &…

pynput模块—键盘鼠标操作和监听

pynput.mouse&#xff1a;包含控制和监控鼠标或者触摸板的类。 pynput.keyboard&#xff1a;包含控制和监控键盘的类。 上面提到的子包都已被引入到pynput库中。要使用上面的子包&#xff0c;从pynput中引入即可。 下面有详细的示例文档。 控制鼠标 使用pynput.mouse控制鼠标&a…

linux的mysql小记

今天试着自己安装mysql数据库&#xff0c;前期准备工作&#xff1a;首先在http://www.mysql.com/downloads/mysql/里面下载两个文件&#xff0c;&#xff08;1&#xff09;MySQL-server-5.6.10-1.linux_glibc2.5.i386.rpm&#xff08;2&#xff09;MySQL-client-5.6.10-1.linux…

一定用得到的免费 C++ 资源,值得收藏!

提到C/C语言很多初学者都觉得&#xff0c;学到中间就进行不下去了&#xff0c;但是如果你最难啃的那几块硬骨头拿下&#xff0c;一切都会顺畅许多&#xff0c;而且C诞生很久了&#xff0c;因此有大量可以免费阅读编程文档。近日&#xff0c;在Quora上发现一份免费的C 资料列表&…

将试用版visual studio 2008升级为正式版 --更新

引用自 pkdoor 升级VS 2005 的方法如果我们不小心安装VS 2008的时候,没有事先更改CDKEY 我们也可以这么做来实现VS 2008的注册在“添加删除”里面选择删除"Microsoft Visual Studio Team System 2008 Team Suite--简体中文",然后在打开的窗口中选择最后一项“添加注册…

课外知识----浏览器存储技术

Cookie Cookie 是指存储在用户本地终端上的数据&#xff0c;同时它是与具体的 Web 页面或者站点相关的。Cookie 数据会自动在 Web 浏览器和 Web 服务器之间传输&#xff0c;也就是说 HTTP 请求发送时&#xff0c;会把保存在该请求域名下的所有 Cookie 值发送给 Web 服务器&…

SQL Server 事务、异常和游标

事务 在数据库中有时候需要把多个步骤的指令当作一个整体来运行&#xff0c;这个整体要么全部成功&#xff0c;要么全部失败&#xff0c;这就需要用到事务。 1、 事务的特点 事务有若干条T-SQL指令组成&#xff0c;并且所有的指令昨晚一个整体提交给数据库系统&#xff0c;执行…

MySQL cast()函数以及json列

在工作中遇到了json列&#xff0c;不清楚如何写SQL&#xff0c;查询了下相关的文档之后总结下&#xff0c;根据json列的值进行区分&#xff0c;列值指的是 json_type(json列)的结果 1、列值为NULL create table t1(c1 int primary key, c2 json);insert into t1 values(4, NU…

算法导论13-1节习题解答

CLRS 13.1-1利用性质画图&#xff0c;略 CLRS 13.1-2是否 CLRS 13.1-3是&#xff0c;因为就根部被改变了&#xff0c;并不与其他性质矛盾。 CLRS 13.1-44&#xff0c;两个子结点都为红色3&#xff0c;两个子结点一红一黑2&#xff0c;两个子结点都为黑 树的叶子的深度将会都一样…

关于z-index的一些问题

div2的z-index为2&#xff0c;但是现在绝对定位的div3明明z-index比它大&#xff0c; 却依旧在这个层之下。原因是z-index是相对同一父元素下叠加时的z轴顺序。 z-index具有继承性&#xff0c;用简单的数学逻辑表示就是&#xff1a;div1的z-index为1&#xff0c;则它 的子元素d…

查询mysql单个分区的方法

os : linux 数据库: musql 8.0.25 今天工作中遇到了如何查询单个分区中数据的问题&#xff0c;记录下以便于后续再次遇到此问题就可以直接查询该文章了。 建表语句和插入数据的SQL语句如下&#xff1a; drop table if exists tt;create table tt (c1 int primary key, c2 va…

2013腾讯编程马拉松初赛(3月20日)

1 第一题 小Q系列故事——屌丝的逆袭 表示这道题基本没什么算法&#xff0c;学过计算机语言的应该都能搞定吧。 2 第二题 小明系列故事——买年货 这道题直接用01背包问题就可以解决了&#xff0c;只是除了钱的限制&#xff0c;还有积分的限制和免费的情况&#xff0c;就是这点…

MySQL中创建partition表的几种方式

OS : linux 数据库&#xff1a;MySQL 8.0.25 MySQL中创建partition表的几种方式如下&#xff0c;这几种方式都是经过验证过的&#xff0c;只需将enginexxx修改即可&#xff1a; 1. PARTITION BY RANGE drop table if exists employees;CREATE TABLE employees (id INT NOT N…

Windows跟Linux的不同处理

1. 时区 1.1 北京时间 Windows&#xff1a;TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"); Linux&#xff1a;TimeZoneInfo tzBeijing TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai"); 1.2 美东时间 Window&#xff1a; TimeZoneI…

我的学习工作经历,一个园林专业中专毕业生的IT之路

魏琼东&#xff0c;男&#xff0c;1983年生人&#xff0c;祖籍甘肃陇南人&#xff0c;首先得感谢我父亲给我取了这么一个好名字&#xff0c;至少我非常喜欢他&#xff0c;因为目前还没有发现和我同名的人。 我是1998-2002年在甘肃林业学校读了四年的园林专业&#xff0c;那四年…

Windows 恢复环境(Windows RE模式)

Windows 恢复环境 (Windows RE) 是一个能修复无法启动操作系统的常见问题的恢复环境。Windows 预安装环境 (Windows PE) 是具有有限服务的最小 Win32 操作系统。Windows RE 建立在 Windows 预安装环境 (Windows PE) 的基础上&#xff0c;并且可以用附加的驱动程序、语言、Windo…

开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别

最近工作中遇到了开源许可证的问题&#xff0c;需要测试基于开源软件开发的本公司产品满足哪些开源协议&#xff0c;网上找了一些关于这方面的解答&#xff0c;在此备份下&#xff1a; 首先借用有心人士的一张相当直观清晰的图来划分各种协议&#xff1a;开源许可证GPL、BSD、M…

数据库知识点

1.左连接&#xff0c;等值连接&#xff0c;自然连接 等值连接&#xff1a;关系R、S,取两者笛卡尔积中属性值相等的元组 自然连接&#xff1a;是一种特殊的等值连接&#xff0c;它要求比较的属性列必须是相同的属性组&#xff0c;并且把结果中重复属性去掉。 左连接&#xff1a;…

SQL Server 索引结构及其使用(一)[转]

一、深入浅出理解索引结构  实际上&#xff0c;您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引&#xff1a;聚集索引&#xff08;clustered index&#xff0c;也称聚类索引、簇集索引&#xff09;和非聚集索引&#xff08;nonclustered index&#xff0c…