Subprocesos múltiples simultáneos de C++: patrón de diseño singleton y bloqueo de verificación doble

Tabla de contenido

1--patrón de diseño Singleton

2--Bloqueo de doble control

3--Uso de std::call_once()


1--patrón de diseño Singleton

        El patrón de diseño singleton requiere una determinada clase para crear como máximo un objeto, que es el objeto singleton (un objeto de instancia globalmente único);

        El patrón de diseño singleton se puede dividir en: ① Estilo perezoso (carga cuando se necesita la instancia), ② Patrón hambriento (carga la instancia desde el principio del programa);

        Si un objeto de clase es complejo y grande, y se puede reutilizar, provocará un gran desperdicio de rendimiento al crear y destruir objetos de clase con frecuencia. Diseñar este objeto de clase como un objeto único a través del patrón de diseño único puede resolver el problema de las preguntas anteriores;

        Por lo general, se recomienda usar clases anidadas para recuperar memoria. El siguiente código muestra cómo usar clases anidadas para destruir objetos y recuperar memoria, y adopta un patrón de diseño perezoso;

#include <iostream>

class MyCAS{
public:
    static MyCAS *GetInstance(){
        if(m_instance == NULL){
            m_instance = new MyCAS(); 
            static CGar c1;
        }
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量

};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;


int main(int argc, char *argv[]){
    MyCAS *sample1 = MyCAS::GetInstance(); // 创建对象,返回该类对象的指针
    MyCAS *sample2 = MyCAS::GetInstance(); // 创建对象,返回该类对象的指针
    std::cout << sample1 << " " << sample2 << std::endl; // 两个指针指向同一个对象

    sample1->func(); // 调用成员函数测试
    return 0;
}

2--Bloqueo de doble control

        Los bloqueos de doble verificación pueden evitar la competencia de datos entre varios subprocesos. El uso de bloqueos de doble verificación en el siguiente código puede evitar que varios subprocesos creen objetos de clase al mismo tiempo (es decir, ejecuten new MyCAS(););

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1; // 互斥量

class MyCAS{
public:
    static MyCAS *GetInstance(){
        // 双重检查锁
        if(m_instance == NULL){
            std::unique_lock<std::mutex> guard1(mutex1); // 自动加锁和解锁
            if(m_instance == NULL){
                m_instance = new MyCAS();
                static CGar c1;
            }
        }
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量
};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;

// 线程入口函数
void mythread(){
    std::cout << "start thread" << std::endl;
    MyCAS *p_a = MyCAS::GetInstance();
    p_a->func();
    std::cout << "thread end" << std::endl;
    return;
}

int main(int argc, char *argv[]){
    std::thread thread1(mythread);
    std::thread thread2(mythread);
    thread1.join();
    thread2.join();
    return 0;
}

Problemas con cerraduras doblemente comprobadas:

        Cuando algunos miembros de un objeto de clase son más complejos, el tiempo de inicialización es relativamente largo; cuando un hilo crea un objeto único, algunos de sus miembros no se inicializarán en el futuro; en este momento, si otros hilos encuentran que el objeto único se ha creado (espacio de direcciones existente), puede optar por llamar a la función miembro del objeto de clase (pero la función miembro compleja no se inicializa en este momento), y se producirá un error;

        En este momento, debe usar la palabra clave volatile para asegurarse de que la función miembro llamada esté completamente inicializada;

3--Uso de std::call_once()

        La función de std::call_once() es garantizar que la función func() solo se llamará una vez ; (es decir, std::call_once() tiene la capacidad de mutex, que consume menos recursos que mutex)

        std::call_once() debe usarse junto con un indicador que determina si se llama a la función del objeto func();

#include <iostream>
#include <thread>
#include <mutex>

std::once_flag g_flag; // 系统定义的标记

class MyCAS{
private:
    static void CreateInstance(){
        m_instance = new MyCAS();
        static CGar c1;
    }

public:
    static MyCAS *GetInstance(){
        // std::call_once 确保创建单例对象的函数只会被调用一次
        std::call_once(g_flag, CreateInstance);
        return m_instance;
    }

    void func(){
        std::cout << "test sample!" << std::endl;
    }

    class CGar{ // 类中套类,用于释放对象
    public:
        ~CGar(){
            if(MyCAS::GetInstance){
                delete MyCAS::m_instance;
                MyCAS::m_instance = NULL;
            }
        }
    };

private:
    MyCAS(){}; // 私有化成员变量,不能通过构造函数来创建对象
    static MyCAS *m_instance; // 静态成员变量
};

// 静态数据成员类外初始化
MyCAS* MyCAS::m_instance = NULL;

// 线程入口函数
void mythread(){
    std::cout << "start thread" << std::endl;
    MyCAS *p_a = MyCAS::GetInstance();
    p_a->func();
    std::cout << "thread end" << std::endl;
    return;
}

int main(int argc, char *argv[]){
    std::thread thread1(mythread);
    std::thread thread2(mythread);
    thread1.join();
    thread2.join();
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_43863869/article/details/132325740
Recomendado
Clasificación