当前位置:网站首页>多线程的互斥锁应用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)。
边栏推荐
猜你喜欢

Install MySQL tutorial under Linux

Install Mysql5.7 under Linux, super detailed and complete tutorial, and cloud mysql connection

已删除

MySQL数据库 ---MySQL表的增删改查(进阶)

The advanced version of the cattle brushing series (search for rotating sorted arrays, inversion of the specified range in the linked list)

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

MySQL mass production of data

用jOOQ 3.17投射类型安全的嵌套表记录

推荐系统-排序层:排序层架构【用户、物品特征处理步骤】

Alibaba Cloud Martial Arts Headline Event Sharing
随机推荐
Install MySQL tutorial under Linux
英文字母间隔突然增大(全角与半角转换)
历史上的今天:Win10 七周年;微软和雅虎的搜索协议;微软发行 NT 4.0
MySQL eight-part text recitation version
mysql慢查询优化
看完《二舅》,我更内耗了
MindSpore:【JupyterLab】查看数据时报错
The 17th "Revitalization Cup" National Youth Vocational Skills Competition - Computer Programmers (Cloud Computing Platform and Operation and Maintenance) Participation Review and Summary
centos7安装mysql8
ResNet18-实现图像分类
VS Code connects to SQL Server
推荐系统:冷启动问题【用户冷启动、物品冷启动、系统冷启动】
推荐系统:评估指标【离线评估指标:RMSE(均方根误差)、AUC、准确率、召回率、F1】【在线评估:A/B测试】【一般要求响应时间<0.5s】
MySQL六脉神剑,SQL通关大总结
Day31 LeetCode
How to copy table structure and table data in MySQL
Witness the magical awakening of the mini world in HUAWEI CLOUD
mysql8 installation under linux
The JDBC programming of the MySQL database
PHP低代码开发平台 V5.0.7新版发布