Mecanismo RAII de aplicación mutex de subprocesos múltiples

¿Qué es el mecanismo RAII?

RAII es la abreviatura de Resource Acquisition Is Initialization (traducido como "Resource Acquisition Is Initialization"). Es un modismo del lenguaje C++ para administrar recursos y evitar fugas de recursos. Este método se basa en el mecanismo de ejecución de los recursos y destructores del constructor .

El enfoque de RAII es usar un objeto de clase para obtener recursos en el constructor del objeto, controlar el acceso a los recursos durante la vida útil del objeto y finalmente liberar los recursos obtenidos cuando el objeto desaparece ;

Los recursos aquí pueden ser identificadores de archivos, memoria, eventos, mutexes, etc., porque los recursos del sistema son limitados, al igual que el petróleo y el mineral de hierro en la naturaleza, no son inagotables. Por lo tanto, en cuanto a la seguridad de la programación, requerimos seguir los siguientes pasos:

  1. recursos de la aplicación
  2. usar recursos
  3. liberar recursos

En el paso 1 y el paso 2, generalmente somos relativamente fáciles de entender, y la liberación de recursos se ignorará fácilmente debido a varias razones de codificación, lo que resultará en recursos del sistema que en realidad no se utilizan, pero no se liberan o causan otros problemas . afectando la utilización de los recursos del sistema Tasa.

Las desventajas de no utilizar el mecanismo RAII

Entonces, ¿por qué recomendamos utilizar el mecanismo RAII para la codificación cuando se trata de la gestión de recursos?

Puede ver los códigos en los siguientes dos artículos. En estos dos artículos, uso directamente la API del sistema para las operaciones de recursos y no uso el mecanismo RAII para la administración de recursos, lo cual es muy inconveniente para la lectura y el mantenimiento de códigos.

Generación y solución de interbloqueo de subprocesos múltiples
Utilice un segmento de código clave para realizar la sincronización de subprocesos

Fragmentos de métodos de codificación obsoletos:

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;
     } 
 }

La razón por la que no se recomienda un método de codificación de este tipo es que EnterCriticalSection/LeaveCriticalSectiondebe usarse en parejas y depende mucho de las personas, lo que no puede resolver fundamentalmente el problema. Si LeaveCriticalSectionla función no se ejecuta u olvida agregar la API, lo hará. causar problemas fácilmente.

Mecanismo RAII de aplicación mutex

Para resolver el problema de manera fundamental y reducir los problemas del sistema de aplicaciones o las fugas de recursos causadas por factores humanos, demuestra cómo aplicar el mecanismo RAII en segmentos de código clave y mutexes para simplificar la codificación de exclusión mutua de subprocesos múltiples.

Inicialización del segmento de código clave e interfaz de bloqueo:

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; 
};

Cree un objeto mutex y una interfaz de bloqueo:

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;
};

Objetos de plantilla de clase, una vez más utilice el mecanismo RAII para gestionar la ocupación y liberación de objetos de bloqueo. Se recomienda simplificar la aplicación de bloqueos y realizar el reciclaje automático de recursos.

template<class T>
class CLockGuard
{
    
    
public:
    CLockGuard(T& locker) :m_lockerObj(locker)
    {
    
    
        m_lockerObj.Lock();
    }

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

Ejemplo concreto:

#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;
}

Efecto de ejecución:
inserte la descripción de la imagen aquí
a juzgar por los resultados de salida, nuestro bloqueo está en vigor y no hay confusión. La clase de plantilla se usa aquí CLockGuardpara simplificar aún más la codificación de bloqueos de subprocesos múltiples, lo que no solo realiza la reutilización del código sino que también garantiza la seguridad de la codificación. De hecho, este método de codificación se ha aplicado a este mecanismo en C++11 lock_guard. Haga clic aquí para ver lock_guard .

Supongo que te gusta

Origin blog.csdn.net/xiao3404/article/details/107520354
Recomendado
Clasificación