pthread_once() 原型:
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
常用于多线程开发中,它的用途是确保在一个进程下的多个线程调用它时,某个资源只被初始化一次。man pthread_once 可以看到:
在多线程下第一次被调用时,会调用 init_routine 函数指针所指向的函数,之后再调用时,不会再调用这个函数指针指向的函数。其依赖 pthread 库保证的线程安全。 部分代码摘自muduo库,如:
currentThreadID.h
#ifndef __H_CURRENT_THREAD_ID_H__
#define __H_CURRENT_THREAD_ID_H__
#include <stdio.h>
#include <stdlib.h>
namespace CurrentThread
{
__thread int tCachedTid = 0;
void cacheTid()
{
if(tCachedTid == 0)
{
printf("first time get tid\n");
tCachedTid = (int)syscall(SYS_gettid);
}
}
inline int tid()
{
if(tCachedTid == 0)
{
cacheTid();
}
return tCachedTid;
}
}
#endif
singleton.h
#include <pthread.h>
#include <typeinfo>
#include "currentThreadID.h"
template <class T>
class Singleton
{
public:
static T* instance()
{
printf("get instance thread id(%lu)\n", CurrentThread::tid());
pthread_once(&mOnce, &Singleton::init);
printf("\033[32mmOnce = %d\033[0m\n", mOnce);
return mInternal;
}
static void init()
{
printf("\033[32mfirst time new %s\033[0m\n", typeid(T).name());
mInternal = new T();
}
private:
Singleton();
~Singleton();
private:
static T *mInternal;
static pthread_once_t mOnce;
};
//类外初始化
template <class T>
T* Singleton<T>::mInternal = nullptr;
template <class T>
pthread_once_t Singleton<T>::mOnce = PTHREAD_ONCE_INIT;
thread.cpp
#include <unistd.h>
#include <thread>
#include <sys/syscall.h>
#include "singleton.h"
#include "currentThreadID.h"
class CConfigManager
{
public:
CConfigManager()
{
}
~CConfigManager()
{
}
void test()
{
printf("test\n\n");
}
};
class CThreadProc
{
public:
CThreadProc()
{
}
~CThreadProc()
{
}
void operator()()
{
int count = 0;
CConfigManager *cfgMrg = Singleton<CConfigManager>::instance();
cfgMrg->test();
while (1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if(count++ == 10)
{
break;
}
}
}
};
int main()
{
CThreadProc threacProc1;
std::thread t(threacProc1);
CThreadProc threacProc2;
std::thread t2(threacProc2);
t.join();
t2.join();
return 0;
}
创建两个线程,调用的是同一个线程函数,在函数里调用Singleton<CConfigManager>::instance();用于获取类 CConfigManager 的单实例,运行结果:
两个线程先后调用了 instance() 函数,但其中的 pthread_once() 中的 init_routine 仅执行了一次。
其中原文中提到若要指定 T 的构造方式,则需要用到模板的特化,在 singleton.h 添加如下代码:
//模板特化
template <>
class Singleton <CConfigManager>
{
public:
static CConfigManager* instance()
{
printf("it's not specially template of CConfigManager\n");
pthread_once(&mOnce, &Singleton::initCfgMgr);
return mInternal;
}
static void initCfgMgr()
{
mInternal = new CConfigManager(10);
}
private:
static CConfigManager *mInternal;
static pthread_once_t mOnce;
};
//类外初始化
CConfigManager *Singleton<CConfigManager>::mInternal = nullptr;
pthread_once_t Singleton<CConfigManager>::mOnce = PTHREAD_ONCE_INIT;
当我们调用 CConfigManager *cfgMrg = Singleton<CConfigManager>::instance(); 时,则调用的是特化的那个模板,因为此时 T=CConfigManager, 而非 CConfigManager 时,则调用普通的那个 template<T> singleton 模板。