当前位置:网站首页>多线程的互斥锁应用RAII机制
多线程的互斥锁应用RAII机制
2022-07-30 19:52:00 【一缕阳光a】
什么是RAII机制
RAII是Resource Acquisition Is Initialization(翻译成 “资源获取即初始化”)的简称,是C++语言的一种管理资源、避免资源泄漏的惯用法,该方法依赖构造函数资和析构函数的执行机制。
RAII的做法是使用一个类对象,在对象的构造函数中获取资源,在对象生命期内控制对资源的访问,最后在对象消失时,其析构函数来释放获取的资源;
这里的资源可以是文件句柄,内存,Event,互斥量等等,由于系统的资源是有限的,就好比自然界的石油,铁矿一样,不是取之不尽,用之不竭的。所以,我们在编程安全上,要求必须遵循以下几个步骤:
申请资源
使用资源
释放资源
在步骤一和步骤二上,我们平时都比较容易把握,而资源的释放会因为种种编码原因容易被忽略,导致系统资源实际没有使用了,但却没有释放或者引发其他问题,影响了系统资源利用率。
没有使用RAII机制的弊端
那么我们为什么涉及资源管理时,建议使用RAII机制进行编码呢?
大家可以查看以下两篇文章中的代码,我在这两篇文章中都是直接使用系统API进行资源操作,没有使用RAII机制进行资源管理,在代码阅读和维护上都很不方便。
多线程死锁的产生和解决
利用关键代码段实现线程同步
不推荐的编码方式片段:
while (TRUE)
{
//等待直到获得指定对象的所有权
EnterCriticalSection(&g_csLock);
//关键代码段-begin
if (g_nIndex++ < nMaxCnt)
{
cout << "Index = "<< g_nIndex << " ";
cout << "Thread2 is runing" << endl;
//权限释放,容易忘记
LeaveCriticalSection(&g_csLock);
}
else
{
//权限释放,容易忘记
LeaveCriticalSection(&g_csLock);
//关键代码段-end
break;
}
}
之所以不推荐这样的编码方式是因为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)。
边栏推荐
- MySQL six-pulse sword, SQL customs clearance summary
- MySQL database --- Addition, deletion, modification and query of MySQL tables (advanced)
- 055 c# print
- PHP低代码开发引擎—表单设计
- iPhone真是十三香?两代产品完全对比,或许上一代更值得买
- What is the difference between a cloud database and an on-premises database?
- Start background services across processes
- The technology is very powerful, do you still need to "manage up"?
- Brush questions record----string
- coming!Dongfang Selection brings goods to the live broadcast of Longjiang agricultural products
猜你喜欢

MySQL数据库————视图和索引

【MindSpore】用coco2017训练Model_zoo上的 yolov4,迭代了两千多batch_size之后报错,大佬们帮忙看看。

MySQL eight-part text recitation version

Different lower_case_table_names settings for server (‘1‘) and data dictionary (‘0‘) 解决方案

VBA runtime error '-2147217900 (80040e14): Automation error

推荐系统:实时性【特征实时性:客户端实时特征(秒级,实时)、流处理平台(分钟级,近实时)、分布式批处理平台(小时/天级,非实时)】【模型实时性:在线学习、增量更新、全量更新】

Google's AlphaFold claims to have predicted almost every protein structure on Earth

推荐系统:概述【架构:用户/物品特征工程---->召回层---->排序层---->测试/评估】【冷启动问题、实时性问题】

Zabbix 5.0 监控教程(一)

MindSpore: CV.Rescale(rescale,shift)中参数rescale和shift的含义?
随机推荐
[hbuilder] cannot run some projects, open the terminal and cannot enter commands
Download and installation of the latest version of MySQL 8.0 under Linux (detailed steps)
利用go制作微信机器人
如何优化OpenSumi终端性能?
PostgreSQL 14.4如何安装使用
MySQL eight-part text recitation version
MySQL性能优化(硬件,系统配置,表结构,SQL语句)
Day31 LeetCode
DCM 中间件家族迎来新成员
【视频】极值理论EVT与R语言应用:GPD模型火灾损失分布分析
MindSpore:【语音识别】DFCNN网络训练loss不收敛
已删除
时间复杂度与空间复杂度
win2003下FTP服务器如何搭建
el-input 只能输入整数(包括正数、负数、0)或者只能输入整数(包括正数、负数、0)和小数
Install MySQL tutorial under Linux
MindSpore:数据处理问题
Niuke.com - Huawei Question Bank (100~108)
湖仓一体电商项目(四):项目数据种类与采集
MySQL performance optimization (hardware, system configuration, table structure, SQL statements)