操作系统和计算机网络

来源:设计模式:单例模式和工厂模式_.net 设计模式 单例工厂_Sinder_小德的博客-CSDN博客

C++之工厂(factory)模式_c++工厂模式_敢敢のwings的博客-CSDN博客

高并发场景下大量TCP链接处于time_wait状态原因及优化思路分析_高并发tcp_Albertliuc的博客-CSDN博客

设计模式

单例模式

1. 单例对象的类只能允许一个实例存在,该类负责创建自己的对象并确保只有一个,相当于整个系统只需要拥有一个全局对象,这样有利于协调整个系统整体的行为。
        主要解决:一个全局使用的类频繁的创建和销毁
        应用场景:(1)网站的计数器,如果网站存在多个计数器,每个用户的访问都刷新一次计数器的值,这样的话计数器的值是难以同步的,但单例模式就不会存在这类问题,而且还可以避免线程安全的问题。(2)多线程的线程池中的设计一般也是采用的单例模式,这是因为线程池需要方便对线程池的池中的线程进行同样的控制,比如一些应用程序的日志应用,或者web中开发中读取配置文件都适合使用单例模式,例如httpApplication就是单例的典型应用。

2.具体实现

饿汉模式当加载类的时候就实例化,且只加载一次,比较浪费空间,不用加互斥锁,效率更高,在类被加载时,就会实例化一个对象并且交给自己引用,避免了线程同步问题,且这个类在整个生命周期只会被加载一次,因此只会创建一个实例。
懒汉模式单例模式被延迟加载,也就是在使用时再实例化,节省空间,但这样实现只能在单线程下,多线程下会出现,在一个线程进入了if(singleton == null)判断语句块时,还没来得及往下执行就又有另一个线程也通过了这个判断语句,这时会产生多个实例,因此需要加上synchronized锁保证线程安全。

双重检测单例模式:基于上述懒汉单例模式进行修改,进行了两次判断,第一次是为了避免不要的实例,第二次是为了进行同步,避免多线程问题。由于singleton = new Singleton()对象的创建在JVM中可能会进行重排序,在多线程下访问存在一定的风险,使用volatile修饰实例变量有效,解决该问题。

锁换成mutex(互斥锁)

volatile:两个线程都要用到某一个变量且该变量的值会被改变时,应该用 volatile 声明,该关键字的作用是防止优化编译器把变量从内存装入 CPU 寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile 的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。

工厂模式

        工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。是一种替代了new的模式。
        应用场景:图片读取器(在项目中,我们经常需要读取不同类型的图片,如bmp、jpeg、png等。使用工厂方法模式,我们可以定义一个抽象的ImageReader工厂接口,具体的BmpReader、JpegReader、PngReader等都实现这个接口并提供自己的创建方法,客户端可以通过调用ImageReader工厂接口的方法来创建相应类型的图片读取器。这样一来,我们可以在不同的图片读取器实现中,对图片的解析方式、压缩方式等进行优化和改进,而不需要修改客户端代码。)

(1)简单工厂模式:对不同类对象的创建进行了一层封装。该模式通过向工厂传递类型来指定要创建的对象。

结构组成:
 工厂类(ShoesFactory):工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。
 抽象产品类(Shoes):用于存放一类特征相似的实现,是具体产品类的继承的父类或实现的接口。
 具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):工厂类所创建的对象就是此具体产品实例。

优点:结构简单,管理方式简单
缺点:扩展性非常差,新增产品的时候,需要去修改工厂类。

(2)工厂方法模式:和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂。

结构组成:
        抽象工厂类(ShoesFactory):工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。
        具体工厂类(NiKeProducer\AdidasProducer\LiNingProducer):继承于抽象工厂,实现创建对应具体产品对象的方式。
        抽象产品类(Shoes):它是具体产品继承的父类(基类)。
        具体产品类(NiKeShoes\AdidasShoes\LiNingShoes):具体工厂所创建的对象,就是此类

优点: 工厂方法模式抽象出了工厂类,并把具体产品对象的创建放到具体工厂类实现。实现了一个工厂生产一类产品,不需要修改工厂类,只需要增加新的具体工厂类即可。
缺点: 每新增一个产品,就需要增加一个对应的产品的具体工厂类。相比简单工厂模式而言,工厂方法模式需要更多的类定义。

(3)抽象工厂模式:上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品,如果需要多类产品,那就需要使用抽象工厂模式。抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。

结构组成与工厂方法模式一样

优点: 提供一个接口,可以创建多个产品族中的产品对象,同一类的多个产品对象不需要创建多个工厂。
缺点: 相比简单工厂模式而言,抽象工厂模式需要更多的类定义。

具体代码实现在C++之工厂(factory)模式_c++工厂模式_敢敢のwings的博客-CSDN博客

TIMEWAIT

过多的 TIME-WAIT 状态主要的危害有两种:

  • 第一是占用系统资源,比如文件描述符、内存资源、CPU 资源等;
  • 第二是占用端口资源,端口资源也是有限的,一般可以开启的端口为 32768~61000,也可以通过 net.ipv4.ip_local_port_range参数指定范围。

        如果客户端(主动发起关闭连接方)的 TIME_WAIT 状态过多,占满了所有端口资源,那么就无法对「目的 IP+ 目的 PORT」都一样的服务端发起连接了,但是被使用的端口,还是可以继续对另外一个服务端发起连接的。
        如果服务端(主动发起关闭连接方)的 TIME_WAIT 状态过多,并不会导致端口资源受限,因为服务端只监听一个端口,而且由于一个四元组唯一确定一个 TCP 连接,因此理论上服务端可以建立很多连接,但是 TCP 连接过多,会占用系统资源,比如文件描述符、内存资源、CPU 资源等。

优化:
(1)客户端层面:我们可以在客户端将HTTP请求头里connection的值设置为:keep-alive。将短连接改成长连接。长连接比短连接从根本上减少了server去主动关闭连接的次数,减少了TIME_WAIT状态连接的产生
(2)服务器层面:我们可以通过修改服务器的系统内核参数来进行优化:
1.允许将TIME_WAIT状态的socket重新用于新的TCP连接,这样的好处就是如果出现大量TIME_WAIT状态的连接,也能够将这些连接占用的端口重新用于新的TCP连接
$ vim /etc/sysctl.confnet.ipv4.tcp_tw_reuse = 1 #默认为0,表示关闭
2.快速回收TIME_WAIT状态的socket
$ vim /etc/sysctl.confnet.ipv4.tcp_tw_recycle = 1#默认为0,表示关闭
3.将MSL值缩减(需谨慎)

中断

        由于中断处理程序既需要运行的快,又需要完成的工作量多,所以将中断处理切分为两部分。中断处理程序是上半部,一旦接收到一个中断,他就立即开始执行,但只做有严格时限的工作。能够被允许稍后执行的工作会推迟到下半部去。

coredump

        进程异常时的一个快照,保存了异常时的内存、寄存器、堆栈等数据。这些数据存储成一个文件,而且是一个ELF文件格式。

delete与delete[]

delete在使用时会经历两步:

  1. 调用指针所指向的对象的析构函数
  2. 调用free函数回收指针所指向的内存

delete[]也是两步:

  1. 调用指针所指向数组中每个对象的析构函数
  2. 调用free函数回收指针所指向的内存

如果对象不显式存在析构函数,如基本数据类型int,char或是自定义数据类型中不显式定义析构函数,这时候delete和delete[]就没有区别,因为不需要调用析构函数。

同步与互斥

当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。
        1.间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。
        2.直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态
        间接相互制约可以称为互斥直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步

线程同步互斥实现方式:

        互斥锁用于保证在某一段时间只有一个线程在执行某些代码。操作流程如下:1)在访问共享资源后临界区域前,对互斥锁进行加锁。2)在访问完成后释放互斥锁导上的锁。3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。
        条件变量用来阻塞线程等待某个事件的发生,并且当等待的事件发生时,阻塞线程会被通知。互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步
        信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。
只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。

RCU锁

        阻塞型同步是指当一个线程到达临界区时,因另外一个线程已经持有访问该共享数据的锁,从而不能获取锁资源而阻塞(睡眠),直到另外一个线程释放锁。如果同步方案采用不当,就会造成死锁以及效率低下等现象。常见阻塞锁有互斥锁、信号量、读写锁等。

        为了降低风险程度和提高程序运行效率,使用不采用锁的同步方案,依照这种设计思路设计的算法称为非阻塞型同步,其本质就是停止一个线程的执行不会阻碍系统中其他执行实体的运行。常见的非阻塞同步实现方案有:无等待、无锁、无障碍。

主流无锁技术:
        Atomic operation(原子操作),在单一、不间断的步骤中读取和更改数据的操作。需要处理器指令支持原子操作:test-and-set (TSR)、compare-and-swap (CAS)、load-link/store-conditional (ll/sc)
        Spin Lock(自旋锁)是一种轻量级的同步方法,一种非阻塞锁。当 lock 操作被阻塞时,并不是把自己挂到一个等待队列,而是死循环 CPU 空转等待其他线程释放锁。
        Seqlock (顺序锁) 是Linux 2.6 内核中引入一种新型锁,它与 spin lock 读写锁非常相似,只是它为写者赋予了较高的优先级。也就是说,即使读者正在读的时候也允许写者继续运行,读者会检查数据是否有更新,如果数据有更新就会重试,因为 seqlock 对写者更有利,只要没有其他写者,写锁总能获取成功。
        RCU(Read-Copy Update)。RCU 首先将需要修改的内容复制出一份副本,然后在副本上进行修改操作。在写者进行修改操作的过程中,旧数据没有做任何更新,不会产生读写竞争,因此依然可以被读者并行访问。当写者修改完成后,写者直接将新数据内存地址替换掉旧数据的内存地址,由于内存地址替换操作是原子的,因此可以保证读写不会产生冲突。内存地址替换后,原有读者访问旧数据,新的读者将访问新数据。当原有读者访问完旧数据,进入静默期后,旧数据将被写者删除回收。当然,通常写者只进行更新、删除指针操作,旧数据内存的回收由另一个线程完成。
        RCU 的关键思想有两个:1)复制后更新;2)延迟回收内存。
典型的RCU更新时序如下:
复制:将需要更新的数据复制到新内存地址;
更新:更新复制数据,这时候操作的新的内存地址;
替换:使用新内存地址指针替换旧数据内存地址指针,此后旧数据将无法被后续读者访问;
等待:所有访问旧数据的读者进入静默期,即访问旧数据完成;
回收:当没有任何持有旧数据结构引用的读者后,安全地回收旧数据内存。
适用场景:RCU适用多读少写场景,RCU和读写锁相似。但RCU的读者占锁没有任何的系统开销。写者与写者之间必须要保持同步,且写者必须要等它之前的读者全部都退出之后才能释放之前的资源。

同步与互斥_什么是同步机制为什么不叫互斥机制_喜欢吃肉的饭的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值