マルチスレッドミューテックスアプリケーションの RAII メカニズム

RAII メカニズムとは何ですか

RAII は、Resource Acquisition Is Initialization (「リソースの取得は初期化」と訳されます) の略語です。これは、リソースを管理し、リソース リークを回避するための C++ 言語のイディオムです。このメソッドは、コンストラクター リソース とデストラクターの実行メカニズムに依存します

RAII のアプローチは、クラス オブジェクトを使用してオブジェクトのコンストラクターでリソースを取得し、オブジェクトの存続期間中にリソースへのアクセスを制御し、最後にオブジェクトが消滅したときに取得したリソースを解放することです

ここでのリソースには、ファイル ハンドル、メモリ、イベント、ミューテックスなどが考えられます。これは、システムのリソースは有限であり、自然界の石油や鉄鉱石と同じように無尽蔵ではないためです。したがって、プログラミングのセキュリティの観点から、次の手順に従う必要があります。

  1. アプリケーションリソース
  2. リソースを使用する
  3. リソースを解放する

ステップ 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を表示します。

おすすめ

転載: blog.csdn.net/xiao3404/article/details/107520354