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