线程安全的单例(参考muduo库)

我的有篇文章有简单的介绍单例的实现,在其中关于多线程安全问题采取的策略是加锁,最近在阅读muduo源码的时候看到更加好的实现方式通过pthread_once.

pthread_once

在多线程的环境下,有些事仅仅需要执行一次。通常会把这个初始化放在mian函数中,当你写一个库的时候就不能放在main函数中了,这个时候你可以考虑pthread_once

int pthread_once(pthread_once_t* once_control, void(*init_routine)(void));
//本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅仅执行一次。

在多线程下,尽管pthread_once会被多个线程调用,但是init_routine函数只会执行一次。

template<typename T>
class Singleton
{
public:
	static T& instance()
	{
		pthread_once(&once_control, &Singleton::init);
		return *value_;
	}
private:
	Singleton();
	~Singleton();
	Singleton& operator=(const Singleton&);
	Singleton(const Singleton&);
	
	static void init()
	{
		value_ = new T();
		::atexit(destroy);
	}
	
	static void destroy()
	{
		typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
		delete value_;
	}
	
	static pthread_once_t once_control;
	static T* value_;
};

上面代码和muduo基本一样,Singleton是一个模板类。通过instance()返回一个类型为T的对象,它确实通过pthread_once达到多线程安全,通过instance返回的对象总是同一个对象。但是如果对象T是能拷贝构造的呢??????

Singleton<string>::instance();//这里创建对象

string str1 = Singleton<string>::instance();这里重新创建一个对象str1

这明显违背了单例设计模式的初衷,除非你阻止类型T的拷贝构造,但这也没啥意义。就像下面这样,我自己创建一个对象。

Singleton<string>::instance();

string str1("123");

其实我目前不大明白作者设计这个模板的目的,可能是给这个库里面的其他模块使用。但是可以参考作者的思路设计一个专用的Singleton类,如下,这个类的构造函数、拷贝构造、拷贝复制都是私有的,只能通过instance返回一个Singleton对象,而且该对象永远是同一个对象,下面有测试代码,可以自行测试

#include <pthread.h>
#include <stdlib.h> //atexit
#include <thread>
#include <unistd.h>
#include <string>

class Singleton
{
public:
	static Singleton& instance()
	{
		pthread_once(&once_control, &Singleton::init);
		return *value_;
	}
	
	const std::string& get_name(){return name_;}
	const int& get_age(){return age_;}
	void init_n_a(std::string name, int age)
	{
		name_ = name;
		age_ = age;
	}
	void set_name(std::string name){name_ = name;}
	void set_age(int age){age_ = age;}
private:
	Singleton(){};
	~Singleton(){};
	Singleton& operator=(const Singleton&);
	Singleton(const Singleton&);
	
	static void init()
	{
		value_ = new Singleton();
		::atexit(destroy);
	}
	
	static void destroy()
	{
		delete value_;
	}
	
	static pthread_once_t once_control;
	static Singleton* value_;
	std::string name_;
	int age_;
};


pthread_once_t Singleton::once_control = PTHREAD_ONCE_INIT;

Singleton* Singleton::value_ = NULL;





void threadfunc(int i)
{
	printf("thread %d start, tid = %d\n", i, std::this_thread::get_id());
	sleep(1);
	printf("tid = %d, %p\n", std::this_thread::get_id(),&Singleton::instance());
	sleep(1);
	printf("tid = %d, name = %s, age = %d\n", 
	std::this_thread::get_id(),
	Singleton::instance().get_name().c_str(),
	Singleton::instance().get_age());
}


int main()
{
	Singleton::instance().init_n_a("xxx", 12);
	printf("tid = %d, %p\n", std::this_thread::get_id(),&Singleton::instance());
	std::thread t1(threadfunc, 1);
	std::thread t2(threadfunc, 2);
	std::thread t3(threadfunc, 3);
	std::thread t4(threadfunc, 4);
	t1.join();
	t2.join();
	t3.join();
	t4.join();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值