C++ 同時マルチスレッド - シングルトン設計パターンとダブル チェック ロック

目次

1 -- シングルトン設計パターン

2--ダブルチェックロック

3 -- std::call_once() の使用


1 -- シングルトン設計パターン

        シングルトン設計パターンでは、特定のクラスが最大 1 つのオブジェクト、つまりシングルトン オブジェクト (グローバルに一意なインスタンス オブジェクト) を作成する必要があります。

        シングルトン設計パターンは、①Lazyスタイル(インスタンスが必要なときにロードする)、②Hungryパターン(プログラムの先頭からインスタンスをロードする)、②Hungry パターン (インスタンスをプログラムの最初からロードする)、に分類できます。

        クラス オブジェクトが複雑で大きく、再利用できる場合、クラス オブジェクトを頻繁に作成および破棄すると、パフォーマンスに大きな無駄が生じます。このクラス オブジェクトをシングルトン デザイン パターンを通じてシングルトン オブジェクトとして設計すると、上記の問題を解決できます。

一般に、ネストされたクラスを        使用してメモリを再利用することが推奨されます。次のコードは、ネストされたクラスを使用してオブジェクトを破棄し、メモリを再利用する方法を示し、遅延設計パターンを採用しています。

#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--ダブルチェックロック

        ダブルチェック ロックを使用すると、複数のスレッド間のデータ競合を回避できます。次のコードでダブルチェック ロックを使用すると、複数のスレッドが同時にクラス オブジェクトを作成する (つまり、 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;
}

二重チェックされたロックの問題:

        クラス オブジェクト内の一部のメンバーがより複雑な場合、初期化時間は比較的長くなります。スレッドがシングルトン オブジェクトを作成するとき、そのメンバーの一部は将来初期化されません。この時点で、他のスレッドがシングルトン オブジェクトを検出した場合、が作成されている (既存のアドレス空間) 場合は、クラス オブジェクトのメンバー関数を呼び出すことを選択できます (ただし、この時点では複合メンバー関数は初期化されていません)。エラーが発生します。

        現時点では、呼び出されたメンバー関数が完全に初期化されていることを確認するために、volatileキーワードを使用する必要があります。

3 -- std::call_once() の使用

        std::call_once() の機能は、関数 func() が 1 回だけ呼び出されるようにすることです(つまり、 std::call_once() には、mutex よりも消費するリソースが少ない mutex の機能があります)。

        std::call_once() は、オブジェクトの関数 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;
}

おすすめ

転載: blog.csdn.net/weixin_43863869/article/details/132325740