RAII メカニズムとは何ですか
RAII は、Resource Acquisition Is Initialization (「リソースの取得は初期化」と訳されます) の略語です。これは、リソースを管理し、リソース リークを回避するための C++ 言語のイディオムです。このメソッドは、コンストラクター リソース とデストラクターの実行メカニズムに依存します。
RAII のアプローチは、クラス オブジェクトを使用してオブジェクトのコンストラクターでリソースを取得し、オブジェクトの存続期間中にリソースへのアクセスを制御し、最後にオブジェクトが消滅したときに取得したリソースを解放することです。
ここでのリソースには、ファイル ハンドル、メモリ、イベント、ミューテックスなどが考えられます。これは、システムのリソースは有限であり、自然界の石油や鉄鉱石と同じように無尽蔵ではないためです。したがって、プログラミングのセキュリティの観点から、次の手順に従う必要があります。
- アプリケーションリソース
- リソースを使用する
- リソースを解放する
ステップ 1 とステップ 2 では、通常は比較的簡単に把握できますが、コーディング上のさまざまな理由によりリソースの解放は簡単に無視され、その結果、実際には使用されていないシステム リソースが解放されなかったり、他の問題が発生したりすることがあります。システム リソースの使用率に影響を与える速度。
RAII メカニズムを使用しない場合の欠点
では、リソース管理に関してエンコードに RAII メカニズムを使用することを推奨するのはなぜでしょうか?
コードは次の 2 つの記事で参照できます。これら 2 つの記事では、リソース操作にシステム 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を表示します。