一 多线程中单例模式的实现方法
在多线程中实现单例模式,最好的办法是在主线程中提前初始化好实例,再开启子线程,从而避免子线程间资源竞争的问题。就是大家所说的饿汉模式:
即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。
class SingletonStatic
{
private:
static const SingletonStatic* m_instance;
SingletonStatic(){}
public:
static const SingletonStatic* getInstance()
{
return m_instance;
}
};
//外部初始化 before invoke main
const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;
由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。
故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。
第二种方法就是在对实例初始化时,加锁,避免线程竞争,即:双重锁定
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
using namespace std;
std::mutex g_mutex;
class Singleton
{
public:
static Singleton *getInstance() // 返回静态数据成员,必须是静态数据成员函数
{
if (itself == nullptr) // 双重锁定,在第一次初始化时,才在多线程中加锁。
{
std::lock_guard<mutex> my_guard(g_mutex);
if (itself == nullptr)
{
itself = new Singleton();
static GC gc; // 让itself能够回收,而不是等待进程退出,再直接回收进程泄漏的内存。
}
}
return itself;
}
class GC
{
public:
~GC()
{
if (Singleton::itself != nullptr)
{
delete Singleton::itself;
Singleton::itself = nullptr;
}
}
};
void print()
{
cout << "hello world!" << endl;
}
private:
Singleton(){} // 私有化构造函数
private:
static Singleton* itself; // 静态成员变量
};
Singleton* Singleton::itself = nullptr;
void mythread()
{
cout << "thread start!" << endl;
// ...
Singleton::getInstance()->print();
cout << "thread end!" << endl;
}
int main()
{
std::thread jobA(mythread);
std::thread jobB(mythread);
jobA.join();
jobB.join();
return 0;
}
代码中为了回收itself, 定义了一个 GC 的内部类。
一 std::call_once()
std::call_once()是一个函数,它的第二个参数是另一个函数名a();它保证了,在多线程中,函数a()只被其中的一个线程调用一次。std::call_once()具备mutex的能力,并且消耗的资源更少。但它需要与一个标记结合使用,std::once_flag;std::call_once()通过std::once_flag来标记函数a()是否已经被执行。调用std::call_once()成功后,就把标记设置为已调用状态。后续再调用std::call_once(),a()函数不会再执行了。
示例:将上面的程序改写
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
std::mutex g_mutex;
std::once_flag g_falg; //标记
class Singleton
{
public:
static void createInstance()
{
if (itself == nullptr)
{
itself = new Singleton();
static GC gc; // 让itself能够回收,而不是等待进程退出,再直接回收进程泄漏的内存。
}
}
static Singleton *getInstance() // 返回静态数据成员,必须是静态数据成员函数
{
std::call_once(g_falg, createInstance); // 两个线程同时运行到这,其中一个线程要等另外一个线程执行完createInstance()
return itself; // 从而保证createInstance只被执行一次
}
class GC
{
public:
~GC()
{
if (Singleton::itself != nullptr)
{
delete Singleton::itself;
Singleton::itself = nullptr;
}
}
};
void print()
{
cout << "hello world!" << endl;
}
private:
Singleton(){} // 私有化构造函数
private:
static Singleton* itself; // 静态成员变量
};
Singleton* Singleton::itself = nullptr;
void mythread()
{
cout << "thread start!" << endl;
// ...
Singleton::getInstance()->print();
cout << "thread end!" << endl;
}
int main()
{
std::thread jobA(mythread);
std::thread jobB(mythread);
jobA.join();
jobB.join();
return 0;
}