互斥锁、条件锁、自旋锁、读写锁
互斥锁
C++11开始引入了多线程库<thread>,其中也包含了互斥锁的API:std::mutex
头文件:< mutex >
类型: std::mutex
用法:在C++中,通过构造std::mutex的实例创建互斥元,调用成员函数lock()来锁定它,调用unlock()来解锁,不过一般不推荐这种做法,标准C++库提供了std::lock_guard类模板,实现了互斥元的RAII惯用语法。std::mutex和std::lock _ guard。都声明在< mutex >头文件中。Class lock_guard是在声明时,自动上锁,在离开作用域之后自动析构解锁。
条件锁(条件变量)
头文件:< condition_variable >
类型:std::condition_variable(只和std::mutex一起工作) 和 std::condition_variable_any(符合类似互斥元的最低标准的任何东西一起工作)。
最为常见的就是在线程池中,初始情况下因为没有任务使得任务队列为空,此时线程池中的线程因为“任务队列为空”这个条件处于阻塞状态。一旦有任务进来,就会以信号量的方式唤醒该线程来处理这个任务。
自旋锁
从“自旋锁”的名字也可以看出来,如果一个线程想要获取一个被使用的自旋锁,那么它会一直占用CPU请求这个自旋锁使得CPU不能去做其他的事情,直到获取这个锁为止,这就是“自旋”的含义。
自旋锁,没有获取到锁的线程会一直循环等待判断资源是否已经释放锁,不用将线程阻塞起来;互斥锁,把未获取到锁的线程阻塞起来,等待重新调度请求。
获取锁的线程一直处于活跃状态,但并没有执行任何有效的任务,使用自旋锁会造成busy-waiting。互斥锁是sleep-waiting
特点
适合场景:
自旋锁的原理比较简单,如果持有锁的线程能在短时间内释放锁资源,那么等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞状态,只需要等一等(自旋),等到持有锁的线程释放锁后即可获取,避免用户进程和内核切换的消耗。
自旋锁的类型:spinlock_t
相关函数:
spin_lock_init(spinlock_t *x); //初始化
spin_lock(x); //只有在获得锁的情况下才返回,否则一直“自旋”
spin_trylock(x); //如立即获得锁则返回真,否则立即返回假
spin_unlock(x); //释放锁
spin_is_locked(x); //该宏用于判断自旋锁x是否已经被某执行单元保持(即被锁),如果是,返回真,否则返回假。
#include <pthread.h>
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
读写锁
对于多读少写的场景可以用读写锁(也叫共享独占锁),来缓解。
读写锁的类型:pthread_rwlock_t
// 声明一个读写锁
pthread_rwlock_t rwlock;
...
// 在读之前加读锁
pthread_rwlock_rdlock(&rwlock);
... 共享资源的读操作
// 读完释放锁
pthread_rwlock_unlock(&rwlock);
// 在写之前加写锁
pthread_rwlock_wrlock(&rwlock);
... 共享资源的写操作
// 写完释放锁
pthread_rwlock_unlock(&rwlock);
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
C++11中有互斥量、条件变量但是并没有引入读写锁。而在C++17中出现了一种新锁:std::shared_mutex。用它可以模拟实现出读写锁
CAS是解决多线程并行情况下使用锁造成性能损耗的一种机制。
https://zhuanlan.zhihu.com/p/400817892
C++内存分配的方式有三种:分别是从静态存储区分配,从栈上分配内存和从堆上分配内存。
从静态存储区域分配的内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。静态分配的区域的生命期是整个软件运行期,就是说从软件运行开始到软件终止退出。只有软件终止运行后,这块内存才会被系统回收。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但 是分配的内存容量有限。在栈中分配的空间的生命期与这个变量所在的函数和类相关。如果是函数中定义的局部变量,那么它的生命期就是函数被调用时,如果函数运行结束,那么这块内存就会被回收。如果是类中的成员变量,则它的生命期与类实例的生命期相同。
理解:系统自动分配,如声明int a;系统自动在栈空间中为a开辟空间。
亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存 期由我们决定,使用非常灵活,但问题也最多。在堆上分配的内存,生命期是从调用new或者malloc开始,到调用delete或者free结束。如果不 掉用delete或者free。则这块空间必须到软件运行结束后才能被系统回收。
理解:程序员申请,并指明大小 c中的malloc,如charp=(char)malloc(10); C++中的new运算符:如int*p2=new int(10);
C++内存布局,也就是一个由c/C++编译的程序占用的内存分为以下几个部分: 1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量值等,其操作方法类似数据结构中的栈。 2、堆区(heap):一般由程序员分配释放,与数据结构中的堆毫无关系,分配方式类似于链表。 3、全局/静态存储区(static):全局变量和静态变量的存储是放在一起的,在程序编译时分配。 4、常量存储区:存放常量字符串。 5、程序代码区:存放函数体(类的成员函数、全局函数)的二进制代码
.bss BSS段(bss segment):通常是指用来存放程序中**未初始化**的全局变量的一块内存区域。BSS段属于静态内存分配。
.RW data数据段(data segment):通常是指用来存放程序中**已初始化**的全局变量的一块内存区域。数据段属于静态内存分配。
.RO data只读数据段:只读数据段是程序使用的一些不会被更改的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要更改,因此只需要放置在只读存储器中即可。
.text代码段(code segment/text segment):通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行之前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。程序段为程序代码在内存中的映射,一个程序可以在内存中有多个副本。
extern置于变量或函数前,用于标示变量或函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。它只要有两个作用:
注意:
extern 和 static
static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;
其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;
extern 和const
const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];**然后在原文件中别忘了定义: const char g_str[] = “123456”;
内存对齐的原则以及作用?
1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。
2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。
3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的"最宽基本类型成员"的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。
4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。
内存对齐的主要作用是:
1、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度”,举例int型数据的跨块存储
struct能包含成员函数吗? 能! struct能继承吗? 能!! struct能实现多态吗? 能!!!
既然这些它都能实现,那它和class还能有什么区别?
最本质的一个区别就是默认的访问控制:
默认的继承访问权限
struct是public的,class是private的。
当然,到底默认是public继承还是private继承,取决于子类而不是基类。
我的意思是,struct可以继承class,同样class也可以继承struct,那么默认的继承访问权限是看子类到底是用的struct还是class。如下:
struct A{};class B : A{}; //private继承 struct C : B{}; //public继承
struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的
1.什么是单例模式?
简单来讲,就是在软件系统中,一个类只允许有一个实例,即只能生成一个对象。怎么实现:该类提供了一个静态的公有函数用于创建或者获取它本身的静态私有对象;类定义中含有一个该类的静态私有对象;单例模式的类只提供私有的构造函数,防止多个实例创建。单例模式又分为懒汉模式和饿汉模式两种。
单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
(1)饿汉
饿汉单例,即在最开始的时候,静态对象就已经创建完成; 设计方法是类中包含一个静态成员指针,该指针指向该类的一个对象,提供一个公有的静态成员方法,返回该对象指针;为了使得对象唯一,还需要将构造函数设为私有,代码如下:
// version 1.3
class Singleton
{
private:
static Singleton instance;
private:
Singleton();
~Singleton();
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance() {
return instance;
}
}
// initialize defaultly
Singleton Singleton::instance;
单例的饿汉实现是线程安全的,因为对象在使用前就已经创建出来了
(2)懒汉
所谓懒汉模式,就是尽可能晚的创建这个对象的实例,即在单例类第一次被引用时将自己初始化;其实C++里很多地方都是类似这样的思想,比如晚绑定,写时拷贝技术等,就是尽量使资源的利用最大化,不要让空闲的人还占着有限的资源。
class Sigletion2
{
Sigletion2()
{
cout << "Sigletion2()" <<endl;
}
static Sigletion2* intance2;
public:
static Sigletion2* GetSigletion2()
{
if (intance2 == NULL)
{
intance2 = new Sigletion2();
cout << "it is once" <<endl;
}
else
{
cout << "it is not once" <<endl;
}
return intance2;
}
};
Sigletion2* Sigletion2:: intance2 = NULL; //先初始化为空,等真正用上这个单例的时候再创建这个例。
但是以上代码存在new出来的对象内存泄露的和多线程竞争问题,当两个线程同时进入if (singleton == null)
,同时创建实例,导致单例模式失效的问题。
(3)懒汉的线程安全问题
加锁,使得线程同步;
class singleton
{
private:
singleton()
{
pthread_mutex_init(&mutex);
}
static singleton* p;
static pthread_mutex_t mutex;
public:
static singleton* initance()
{
if (p == NULL) //p != NULL,说明对象已经创建出来了,直接返回对象的指针,没必要在加锁解锁浪费时间。
{
pthread_mutex_lock(&mutex);
if (p == NULL)
{
p = new singleton();
}
pthread_mutex_unlock(&mutex);
}
return p;
}
};
pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
(4)懒汉模式 - 现代c++11-优雅的多线程版本
双重检测模式和私有嵌套类Delete实现起来代码量略长,c++11标准下,《Effective C++》提出了一种更优雅简易的多线程单例模式实现,使用函数内的 local static ,即static静态局部变量的初始化,编译器会自动加锁和解锁这样,只有当第一次访问getInstance()方法时static静态局部变量才创建实例。
class Singleton{
private:
Singleton(){printf("Singeleton init \n");};
~Singleton(){printf("delete Singeleton \n");};
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) =delete;
public:
static Singleton* GetInstance(){
static Singleton instance; //函数内的静态局部变量,第一次访问才初始化,程序结束,自动释放。
return &instance;
}
};
工厂模式,指的是封装对象的创建过程,并将创建过程和操作过程分离(解耦),用户(创建者)无需关心具体过程(最少知识原则),就像一个工厂生产产品一样,以便批量管理对象的创建,提高程序的可以维护性和扩展性。
//由一个工厂生产A,B,C
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef enum ProductType
{
TypeA,
TypeB,
TypeC
}ProductType_t;
/* 产品抽象基类 */
class Product
{
public:
virtual void printf() = 0;
};
class ProductA : public Product
{
public:
void printf()
{
cout<<"Create productA"<<endl;
}
};
class ProductB : public Product
{
public:
void printf()
{
cout<<"Create productB"<<endl;
}
};
class ProductC : public Product
{
public:
void printf()
{
cout<<"Create productC"<<endl;
}
};
/* 工厂类 */
class Factory
{
public:
Product* CreateProduct(ProductType_t type);
};
Product* Factory::CreateProduct(ProductType_t type)
{
Product *a = NULL;
switch (type)
{
case TypeA:
a = new ProductA();
break;
case TypeB:
a = new ProductB();
break;
case TypeC:
a = new ProductC();
break;
default:
break;
}
return a;
}
int main(int argc, char **argv)
{
Factory productCreator;
Product *productA;
Product *productB;
Product *productC;
productA = productCreator.CreateProduct(TypeA);
productB = productCreator.CreateProduct(TypeB);
productC = productCreator.CreateProduct(TypeC);
if(productA != NULL)
{
productA->printf();
delete productA;
productA=NULL;
}
if(productB != NULL)
{
productB->printf();
delete productB;
productB=NULL;
}
if(productC != NULL)
{
productC->printf();
delete productC;
productC=NULL;
}
return 0;
}
一个基类工厂,三个子工厂分别生产A,B,C
#include <iostream>
#include <stdlib.h>
using namespace std;
/* 产品抽象基类 */
class Product
{
public:
virtual void printf() = 0;
};
class ProductA : public Product
{
public:
void printf()
{
cout<<"Create productA"<<endl;
}
};
class ProductB : public Product
{
public:
void printf()
{
cout<<"Create productB"<<endl;
}
};
class ProductC : public Product
{
public:
void printf()
{
cout<<"Create productC"<<endl;
}
};
/* 工厂类 */
class Factory
{
public:
virtual Product* CreateProduct()=0;
};
class FactoryA:public Factory
{
public:
Product *CreateProduct()
{
return new ProductA();
}
};
class FactoryB:public Factory
{
public:
Product *CreateProduct()
{
return new ProductB();
}
};
class FactoryC:public Factory
{
public:
Product *CreateProduct()
{
return new ProductC();
}
};
int main(int argc, char **argv)
{
Factory *factoryA;
Factory *factoryB;
Factory *factoryC;
Product *productA;
Product *productB;
Product *productC;
factoryA = new FactoryA();
if(factoryA != NULL)
{
productA = factoryA->CreateProduct();
if (productA != NULL)
{
productA->printf();
delete productA;
productA = NULL;
}
delete factoryA;
factoryA = NULL;
}
factoryB = new FactoryB();
if(factoryB != NULL)
{
productB = factoryB->CreateProduct();
if (productB != NULL)
{
productB->printf();
delete productB;
productB = NULL;
}
delete factoryA;
factoryA = NULL;
}
factoryC = new FactoryC();
if(factoryC != NULL)
{
productC = factoryC->CreateProduct();
if (productC != NULL)
{
productC->printf();
delete productC;
productC = NULL;
}
delete factoryC;
factoryC = NULL;
}
return 0;
}
//A,B,C都有两种型号0,1,两个工厂分别负责生产0型号的a,b,c和1型号的a,b,c
#include <iostream>
#include <stdlib.h>
using namespace std;
/* 产品A抽象基类 */
class ProductA
{
public:
virtual void printf() = 0;
};
/* 产品类A0 */
class ProductA0 : public ProductA
{
public:
void printf()
{
cout<<"Create productA0"<<endl;
}
};
/* 产品类A1 */
class ProductA1 : public ProductA
{
public:
void printf()
{
cout<<"Create productA1"<<endl;
}
};
/* 产品B抽象基类 */
class ProductB
{
public:
virtual void printf() = 0;
};
/* 产品类B0 */
class ProductB0 : public ProductB
{
public:
void printf()
{
cout<<"Create productB0"<<endl;
}
};
/* 产品类B1 */
class ProductB1 : public ProductB
{
public:
void printf()
{
cout<<"Create productB1"<<endl;
}
};
/* 工厂类 */
class Factory
{
public:
virtual ProductA* CreateProductA()=0;
virtual ProductB* CreateProductB()=0;
};
/* 工厂类0,专门生产0类产品 */
class Factory0:public Factory
{
public:
ProductA *CreateProductA()
{
return new ProductA0();
}
ProductB *CreateProductB()
{
return new ProductB0();
}
};
/* 工厂类1,专门生产1类产品 */
class Factory1:public Factory
{
public:
ProductA *CreateProductA()
{
return new ProductA1();
}
ProductB *CreateProductB()
{
return new ProductB1();
}
};
int main(int argc, char **argv)
{
Factory *factory0;
Factory *factory1;
ProductA *productA0;
ProductA *productA1;
ProductB *productB0;
ProductB *productB1;
factory0 = new Factory0();
if(factory0 != NULL)
{
productA0 = factory0->CreateProductA();
if (productA0 != NULL)
{
productA0->printf();
delete productA0;
productA0 = NULL;
}
productB0 = factory0->CreateProductB();
if (productB0 != NULL)
{
productB0->printf();
delete productB0;
productB0 = NULL;
}
delete factory0;
factory0 = NULL;
}
factory1 = new Factory1();
if(factory1 != NULL)
{
productA1 = factory1->CreateProductA();
if (productA1 != NULL)
{
productA1->printf();
delete productA1;
productA1 = NULL;
}
productB1 = factory1->CreateProductB();
if (productB1 != NULL)
{
productB1->printf();
delete productB1;
productB1 = NULL;
}
delete factory1;
factory1 = NULL;
}
return 0;
}
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有