「技术笔记」Linux高并发编程之单例模式
[TOC]
单例模式作为最常用的设计模式之一,用来保证一个类仅有一个实例化对象,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
1、单例模式的实现思路
单例模式单例模式的实现思路一般有如下两步:
(1)私有化类的构造函数,以防止外界创建单例类的对象;
(2)使用类的私有静态指针变量指向类的唯一实例,并用一个公有静态方法获取该实例。
根据类的唯一实例初始化的时机,可以将单例模式的实现分为:懒汉模式和饿汉模式。
懒汉模式:非常懒,不用的时候不去初始化,只在第一次被调用时才进行初始化;
饿汉模式:迫不及待,即使还没有程序调用它,其已提前初始化等待被调用
2、单线程的单例模式
我们实现一个经典的懒汉模式的单例模式如下:
1 |
|
我们实现一个经典的饿汉模式的单例模式如下:
1 |
|
可以看到,所谓的懒汉模式和饿汉模式的区别非常容易理解。
3、多线程的单例模式
在多线程中,需要考虑共享资源的安全性,使用互斥锁
(1)经典的线程安全懒汉模式
单例模式有两种实现方法,分别是懒汉模式和饿汉模式。顾名思义,。
1 |
|
为什么要用双检测,只检测一次不行吗?
如果只检测一次,在每次调用获取实例的方法时,都需要加锁,这将严重影响程序性能。双层检测可以有效避免这种情况,仅在第一次创建单例的时候加锁,其他时候都不再符合NULL == p的情况,直接返回已创建好的实例。
(2)局部静态变量之线程安全懒汉模式
前面的双检测锁模式,写起来不太优雅,《Effective C++》(Item 04)中的提出另一种更优雅的单例模式实现,使用函数内的局部静态对象,这种方法不用加锁和解锁操作。它不用加锁是因为在C++11之后,编译器会保证局部静态变量的线程安全性,所以不需要程序员额外为局部静态变量设置线程安全性。
1 | class Singleton{ |
类的访问权限
c++的访问权限(也叫访问级别):指类外和子类对类内成员的访问权限。c++成员的默认访问权限是private。
访问权限 | 类外(实例化对象) | 类内成员 | 子类成员 | 友元函数 | 友元类 |
---|---|---|---|---|---|
public | 可访问 | 可访问 | 可访问 | 可访问 | 可访问 |
protected | 不可访问 | 可访问 | 可访问 | 可访问 | 可访问 |
private | 不可访问 | 可访问 | 不可访问 | 可访问 | 可访问 |
在c++中,鼓励将所有数据声明为private,想要访问数据则通过单独定义public的成员函数来访问,public为公有的成员函数。
static关键字
static是c++的一个限定符,用来控制某个变量的存储方式和可见性。static可以修饰变量(全局变量、局部变量、类成员变量等),类成员函数、普通函数。所有的静态变量都存储在c++的全局数据区,在声明处被初始化,如果没有指明初始值就自动初始化为0,一旦初始化就直至程序结束才释放内存。使用场景如下:
静态全局变量:只能在本文件中访问,不能在其他文件中访问,即便是extern外部声明也不可以(外部可见性缩小)。 |
---|
静态局部变量在首次执行时初始化,直至程序运行结束后才释放(生命周期延长)。 |
类的静态成员变量必须在类外声明,且类的多个对象共享同一个静态成员变量。 |
类的静态成员函数只能调用静态成员变量(函数内没有this指针)。 |
类的静态成员函数
1)静态成员函数只能调用类的静态成员变量或者静态成员函数。
2)静态成员函数在类外定义时,不能加static修饰,否则出错。
3)在类外可以通过对象名或者类名来调用类的静态成员函数。
4)非静态成员函数可以任意地访问静态成员函数和静态数据成员。
内联函数inline
(问题:inline关键字的作用是什么) inline是c++的一个限定符,用来显示声明一个函数为内联函数,类中定义的函数都默认为内联函数。
内联函数可以减少函数调用的开销,提高程序执行的效率。编译器处理内联函数时,不会单独进行函数调用,而是在编译期间直接将整个函数体的代码插入调用语句处,就像整个函数体在调用处被重写了一遍一样,这个过程发生在编译期间。显然使用内联函数会使最终可执行程序的体积增加,因为内联函数是以空间换取时间。
(什么时候需要使用inline关键字) 内联函数适用于小而简单、执行很快的函数,如果一个函数非常庞大或者需要消耗大量时间,那么将其声明为内联函数虽然节省了函数调用的时间,但是却让程序体积增加了更多,这样程序执行速度很可能反而会下降。现代c++编译器提供了内联函数的保护机制,一般程序员声明的内联函数只是给编译器的建议,具体编译器是否真的按照内联函数的方式处理可能由内部算法决定。