当前位置: 首页 > news >正文

多线程编程:从日志到单例模式全解析

目录

一:日志与策略模式

二:线程池设计

三:线程安全和重入问题

四:线程安全的单例模式

4.1单例模式的特点

4.2饿汉实现方式和懒汉实现方式

4.3饿汉方式实现单例模式

4.4懒汉方式实现单例模式

4.5懒汉方式实现单例模式(线程安全版本)

4.6单例线程池的实现

五:常见锁概念

5.1死锁

5.2死锁四个必要条件

六:STL,智能指针和线程安全


一:日志与策略模式

对一些经典的常见的场景,给定了一些对应的解决方案,这个就是设计模式

日志认识: 计算机中的日志是记录系统和软件运行中发生事件的文件,主要作用是监控运行状态、记录异常信息,帮助快速定位问题并支持程序员进行问题修复。它是系统维护、故障排查和安全管理的重要工具。

日志格式以下几个指标是必须得有的 :时间戳,日志等级 ,日志内容

以下几个指标是可选的:文件名行号 , 进程,线程相关id信息等 

日志有现成的解决方案,如:spdlog、glog、Boost.Log、Log4cxx等等

二:线程池设计

线程池: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池的应用场景:

1.需要大量的线程来完成任务,且完成任务的时间比较短。比如WEB服务器完成网页请求这样的任 务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象⼀个热门网站的点击次数。但对于长时间的任务,比如⼀个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

2.对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

3.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误

线程池的种类

a. 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执行任务对象中 的任务接口

b. 浮动线程池,其他同上

三:线程安全和重入问题

概念:线程安全:就是多个线程在访问共享资源时,能够正确地执行,不会相互干扰或破坏彼此的执行结 果。一般而言,多个线程并发同一段只有局部变量的代码时,不会出现不同的结果。但是对全局变量或者静态变量进行操作,并且没有锁保护的情况下,容易出现该问题。

重入:同⼀个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入, 我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。

重入其实可以分为两种情况 :多线程重入函数  信号导致⼀个执行流重复进入函数

常见的线程不安全的情况 :1. 不保护共享变量的函数 2. 函数状态随着被调用,状态发生变化的函数 3.返回指向静态变量指针的函数 4.调用线程不安全函数的函数

常见的线程安全的情况:1.每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一般来说这些线程是安全的 2.类或者接口对于线程来说都是原子操作 3.多个线程之间的切换不会导致该接口的执行结果存在二义性

常见不可重入的情况 :1.调用了malloc/free函数,因为malloc函数 是用全局链表来管理堆的 2. 调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构 3.可重入函数体内使用了静态的数据结构

常见的可重入的情况 :1.不使用全局变量或静态变量 2.不使用 malloc或者new开辟出的空间3.不调用不可重入函数 4. 不返回静态或全局数据,所有数据都有函数的调用者提供 5.使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据

可重入与线程安全联系 :

函数是可重入的,那就是线程安全的(其实知道这⼀句话就够了)

函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题

如果⼀个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

可重入与线程安全区别

可重入函数是线程安全函数的⼀种

线程安全不一定是可重入的,而可重入函数则⼀定是线程安全的。

如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生死锁,因此是不可重入的。

如果不考虑信号导致一个执行流重复进⼊函数这种重入情况,线程安全和重入在安全角度不做区分

但是线程安全侧重说明线程访问公共资源的安全情况,表现的是并发线程的特点

可重入描述的是一个函数是否能被重复进入,表示的是函数的特点

四:线程安全的单例模式

4.1单例模式的特点

某些类,只应该具有一个对象(实例),就称之为单例。在很多服务器开发场景中,经常需要让服务器加载很多的数据(上百G)到内存中。此时往往要用一个单例的类来管理这些数据。

4.2饿汉实现方式和懒汉实现方式

 洗碗的例子:

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下⼀顿吃的时候可以立刻拿着碗就能吃饭

吃完饭, 先把碗放下, 然后下⼀顿饭用到这个碗了再洗碗, 就是懒汉方式

懒汉方式最核心的思想是"延时加载".从而能够优化服务器的启动速度。

4.3饿汉方式实现单例模式

template <typename T>
class Singleton {static T data;
public:static T* GetInstance() {return &data;}
};

每次使用的时候,都会立刻创建一个对象,不管后面是否使用这个对象

4.4懒汉方式实现单例模式

template <typename T>
class Singleton
{static T *inst;public:static T *GetInstance(){if (inst == NULL){inst = new T();}return inst;}
};

每次实现创建一个结构体指针,指向对象为空,只有开始使用这个结构体里面的函数时,才创建一个对象,而且之后再调用结构体函数时,不会再创建新的对象,一直使用这一个对象。

存在一个严重的问题,线程不安全.第一次调用GetInstance的时候,如果两个线程同时调用,可能会创建出两份T对象的实例.但是后续再次调用就没有问题了.

4.5懒汉方式实现单例模式(线程安全版本)

// 懒汉模式, 线程安全
template <typename T>
class Singleton
{volatile static T *inst; // 需要设置 volatile 关键字, 否则可能被编译器优化.static std::mutex lock;public:static T *GetInstance(){if (inst == NULL){                // 双重判定空指针, 降低锁冲突的概率, 提⾼性能.lock.lock(); // 使⽤互斥锁, 保证多线程情况下也只调⽤⼀次 new.if (inst == NULL){inst = new T();}lock.unlock();}return inst;}
};

注意事项:1. 加锁解锁的位置 2. 双重 if 判定,避免不必要的锁竞争 3. volatile关键字防止过度优化

4.6单例线程池的实现

其他部分同上面进程池的实现

五:常见锁概念

5.1死锁

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会 释放的资源而处于的一种永久等待状态。

为了方便表述,假设现在线程A,线程B必须同时持有锁1和锁2,才能进行后续资源的访问

申请⼀把锁是原子的,但是申请两把锁就不一定了

造成的结果是:

5.2死锁四个必要条件

1. 互斥条件:一个资源每次只能被一个执行流使用 

2.请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放

3.不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺

4.循环等待条件:若干执行流之间形成⼀种头尾相接的循环等待资源的关系

六:STL,智能指针和线程安全

1.STL中的容器是否是线程安全的?

不是。原因是,STL的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,会对性能造成巨大的影响.而且对于不同的容器,加锁方式的不同,性能可能也不同(例如hash表的锁表和锁桶)。因此STL默认不是线程安全.如果需要在多线程环境下使用,往往需要调用者自行保证线程安全.

2.智能指针是否是线程安全的?

对于 unique_ptr 由于只是在当前代码块范围内生效,因此不涉及线程安全问题。即是线程安全的

对于shared_ptr,多个对象需要共用一个引用计数变量,所以会存在线程安全问题。但是标准库实现的时候考虑到了这个问题,基于原子操作(CAS)的方式保证shared_ptr能够高效,原子的操作引用计数,所以也是安全的

http://www.dtcms.com/a/589796.html

相关文章:

  • 邢台市建设局网站定制网站开发的目的是什么
  • 还在自制工作标准品或对照品吗?标准物质供应商推荐
  • 机器学习(4) cost function(代价函数)
  • 翻译《The Old New Thing》- 为什么 SHFormatDateTime 要接收一个未对齐的 FILETIME?
  • 企业网站怎么做的好看在wordpress添加算法
  • 基于「YOLO目标检测 + 多模态AI分析」的医学骨折检测分析系统(vue+flask+数据集+模型训练)
  • linux31 网络编程TCP协议
  • 南昌 网站建设黄山网站建设公司
  • 深入解析:动画组件为何必须使用useCallback
  • 深度强化学习算法详解:从理论到实践
  • 4.1.8 文件系统基础【2011统考真题】
  • 行业网站开发互联网广告平台有哪些
  • 做网站自己上传电影要多大服务器电商是做什么的?
  • 零基础学JAVA--Day27(注释+异常+异常处理方法)
  • 新华网站建设设计漂亮的网站
  • Linux下的编译器gcc/g++
  • 【Redis|第一篇】基础篇
  • 嵌入式回调:弱函数与函数指针的实战解析
  • 网站建设技术支持包括哪些小工程施工合同协议书
  • 掌握RAG系统的七个优秀GitHub存储库
  • 网站开发面试都会问什么问题网站开发的阶段流程图
  • 如何将废弃笔记本搭建成服务器:使用花生壳内网穿透实现公网访问
  • Linux网络编程:应用层协议HTTP
  • 网站按域名跳转不同的页面网站建设面谈话术
  • Photoshop - Photoshop 工具栏(25)仿制图章工具
  • Java 会话技术、Cookie、JWT令牌、过滤器Filter、拦截器Interceptor
  • 简单理解:ADC(模数转换)采集的滤波算法
  • WASM 3.0 两大领域实战:SvelteKit前端新范式(完整版)
  • WebForms ArrayList 深入解析
  • 免费建站网站建设wordpress4.9.1加速