[Linux#46][线程->网络] 单例模式 | 饿汉与懒汉 | 自旋锁 |读写锁 | 网络基础 | 书单

news2024/11/15 9:41:13

目录

1.线程安全

1.1 线程安全的单例模式

1.2 饿汉与懒汉实现方式:

实操

2.锁

3.读者写者问题

实操

4.网络基础

4.1 初始协议

书单:


1.线程安全

STL中的容器和智能指针的线程安全性整理如下:
STL容器线程安全性:

  • 状态:STL中的容器默认不是线程安全的。
  • 原因:STL的设计目标是追求极致的性能。引入线程安全机制(如加锁)会显著影响性能。此外,不同容器的加锁策略可能导致不同的性能表现(例如,哈希表的锁表与锁桶)。
  • 解决方案:在多线程环境中使用STL容器时,需要调用者自行确保线程安全。

智能指针线程安全性:

  • unique_ptr:由于其作用范围限定在当前代码块内,不涉及线程安全问题。
  • shared_ptr:由于多个对象可能共享同一个引用计数变量,存在线程安全问题。
  • 解决方案:标准库在实现shared_ptr时,采用了基于原子操作(如CAS)的方式来保证引用计数的操作既高效又原子,从而确保线程安全。

1.1 线程安全的单例模式


什么是单例模式:
单例模式是一种“经典的、常用的、常考的”设计模式。它是设计模式的一种,旨在为某些常见场景提供标准化的解决方案。
单例模式的特点:

  • 某些类只应具有一个对象(实例),这样的类称为单例。
  • 例如,一个男性只能有一个妻子。在服务器开发中,常用于管理大量数据(如上百G)的单例类。

1.2 饿汉与懒汉实现方式:

  • 饿汉方式:类比于吃完饭后立即洗碗,以便下次吃饭时可以直接使用。在程序中,饿汉方式是在类加载时就立即初始化并创建单例对象。
  • 懒汉方式:类比于吃完饭后暂时不洗碗,等到下次需要用时再洗。懒汉方式的核心是“延时加载”,即直到第一次使用时才创建单例对象,以优化服务器启动速度

饿汉方式实现单例模式:

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

只要通过Singleton类来使用T对象,就能保证进程中只有一个T对象的实例。
懒汉方式实现单例模式:

调用时,发现不存在,才对指针进行 new

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

存在问题:线程不安全。在第一次调用GetInstance时,如果有两个线程同时调用,可能会创建出两个T对象的实例。
懒汉方式实现单例模式(线程安全版本):

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关键字防止过度优化。

例如针对 全局变量 ,其生命周期随进程。

  • 饿汉:一开始就创建
  • 懒汉:使用指针,调用的时候再 new

懒汉模式中,为何GetInstance要设置成静态?

  • 正常来说非静态成员函数是可以通过对象访问到静态成员变量的,这个没有问题
  • 主要是单例对象在实例化出对象前没有对象可以给你借由它访问到那个非静态成员函数,自然也就没办法访问到静态成员变量了
  • 所以将GetInstance设置成静态成员函数,即可在没有对象时,通过类名::函数名直接调用

需要的时候才创建,懒汉模式

实操

将线程池改成单例,懒汉模式

构造方法一定是要有的,单例版的线程池,就是只获取一个线程池,将可能违背单例的部分私有化,不能被对象调用,就能保证多个对象只有一个了

多线程调用单例呢?需要加锁

tp本身就是一份公共资源,如果两个线程同时调用, 可能会创建出两份 ,T 对象的实例。因此我们需要在创建空间时加锁。

public:
static ThreadPool<T> *GetInstance()
    {
        if (nullptr == tp_) // ???
        {
            pthread_mutex_lock(&lock_);
            if (nullptr == tp_)
            {
                std::cout << "log: singleton create done first!" << std::endl;
                tp_ = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&lock_);
        }

        return tp_;
    }

private:
    ThreadPool(int num = defalutnum) : threads_(num)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    ThreadPool(const ThreadPool<T> &) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
 

static ThreadPool<T> *tp_;

优化多线程的判断情况:

多线程创建单例👆画图思考

  1. 创建静态锁
template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
  1. 优化:即全局如果已经被其他线程创建了,就不必要再线性竞争去创建了。所以 if 判断是否存在后再加锁
 if (nullptr == tp_) //加锁
        {
            pthread_mutex_lock(&lock_);

2.自旋锁

其他常见的各种锁

  • 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。(平时用的 互斥锁 都是这个)
  • 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
  • CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  • 自旋锁,公平锁,非公平锁?

自旋锁

信号非阻塞轮询处的故事续接

张三约李四出去吃饭,李四说等一下,张三这是注意到旁边有一家网吧

张三是去网吧,还是在楼下等李四取决于什么?等待时间长短

这和我们线程等待共享资源是一样的?取决于线程需要等待临界区(李四)的时间长短

  • 长:挂起等待锁(eg.互斥)
  • 短:自旋锁

举例:

在楼下等待一会打个电话,一会打个电话,不断地询问好了吗,叫做自旋

我们之前使用到的条件变量的队列等待,是一种挂起的操作

如何实现自旋

  1. 互斥锁的 try 接口
  2. 系统接口 pthread_spin_t

初始化销毁

加锁

解锁

理解:

  • 底层帮我们封装了while循环,实现不断地询问
  • spin_trylock失败了就返回,就相当于互斥锁了

3.读者写者问题

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。

生产消费模型上变形了一点点

也遵循 321 原则

  • 3 种关系:写写(互斥),写读(互斥,同步),读读(共享,没什么关系)
  • 2 种角色:读者 s,写者 s,线程承担
  • 1 个交易场所:数据交换的地点

生产消费和读写的区别?

  • 消费者是拿走,互斥的
  • 读读是共享看的

实操

读写锁 pthread_rwlock_t

初始化销毁

读加锁
未来读的角色和写的角色是两种角色,读者采用读加锁的方式

写加锁

解锁

无论是读者还是写者最后不想用了都采用这个方案进行解锁

理论理解:读多写少的情况--默认读者优先(写者容易形成饥饿。

现在就有个问题读写者原理很清楚了,但是读写锁是怎么做到给读加锁写加锁的呢?

不过目前我们知道了,在任意一个时刻,只允许一个写者写入,但是可能允许多个读者读取(写者阻塞)。

写者在写的时候不允许其他写者写也不允许读着读,而读者在读的时候允许其他读者一起读但不允许写着写。

因为我们的读写锁是一个结构体所以它的内部可能包含了对应的读锁,写锁,还有读计数器。然后再进行对应的操作时使用特定的一套方案进行相关的操作。

在某些并发编程场景中,读写锁(reader-writer lock)被用来允许多个读者(不会修改数据的线程)同时访问共享资源,但在写者(可能会修改数据的线程)访问时,则需要独占访问。读写锁通常默认采用读者优先的策略,但有时候写者优先的策略更加合适。

场景描述:

  • 假设有10个读者和1个写者。
  • 前5个读者已经读取了数据,而第6个和第7个读者正在路上。
  • 在某个时刻,一个读者和一个写者同时到达。
  • 在传统的读者优先策略下,读者会先获得访问权限。
  • 在写者优先的策略下,已经获得访问权限的读者可以继续读取,但新到达的读者需要等待,直到写者完成写入操作。

写者优先策略:

  • 写者优先意味着一旦写者请求访问,新到达的读者必须等待,直到所有写操作完成。
  • 已经在读取的读者可以继续,但新的读者必须等待。
  • 这种策略可以避免写者饥饿,即写者长时间等待的情况。

设置读写优先

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
PTHREAD_RWLOCK_PREFER_READER_NP 一致
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/

4.网络基础

  • 网络的本质:传递数据
  • 系统的本质:处理数据

收集,检测,决策的三个人,要基于数据进行协作

局域网 LAN: 计算机数量更多了, 通过交换机和路由器连接在一起;

广域网 WAN: 将远隔千里的计算机都连在一起;

所谓 "局域网" 和 "广域网" 只是一个相对的概念. 比如, 我们有 "天朝特色" 的广域网, 也 可以看做一个比较大的局域网

  • 我国在网络服务技术处于世界领先地位
  • 每个国家都有自己的网络运营商。例如中国的电信,移动,联通,欧洲的诺基亚...
  • 计算机是人的工具,人要协同工作,注定了网络的产生是必然的.

4.1 初始协议

  • "协议" 是一种约定.
  • 电报响一声、两声、三声都代表不同的含义。而这个含义不用解释,双方早就已经有了共识了。-------->这就是我们的约定,而这种约定就是协议。而这种协议的约定是为了减少通信成本!

主机是对称的,都例如存在如下的问题:

  • 如何处理发来的数据--https/http/ftp/smtp...
  • 长距离传输的数据丢失问题--tcp 协议
  • 如何定位的主机问题--ip 协议
  • 你怎么保证你的数据能准确的到达下一个设备--数据链路层

所有网络的问题:本质都是传输距离变长,所需要的设备就会变多

在要传输的数据之上,还多了一些数据,就像我们的快递盒和快递单一样,这就是协议报头,其表现形式就是结构体对象

下篇文章将继续讲解,两台设备如何实现对定义的 同一对象 实现传输认识~


书单:

从下篇文章开始我们将要从正式系统横跨到网络的学习了,因此有些书我们就可以读起来了。

操作系统

原理:

  • 《操作系统精髓与设计原理》、《现代操作系统》

Linux原理方面的书:

  • 《Linux内核设计与实现》–陈莉君、《深入理解Linux内核》(选读–不作为重点)

Linux编程方面的书:

  • 《Linux高性能服务器编程》、《Unix环境高级编程》

体系结构:

  • 《深入理解计算机系统》

对于系统学过前面的知识建议读书顺序:先编程,后原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zdppy+vue3+onlyoffice文档管理系统实战 20240823上课笔记 Python缓存框架的进一步封装

上节课遗留问题 封装一些简单的方法&#xff0c;比如&#xff1a; set&#xff1a;设置缓存&#xff0c;带默认超时时间get&#xff1a;获取缓存delete&#xff1a;删除缓存delete_all&#xff1a;清空缓存 封装set方法 基本方法 from zdppy_cache import Cache# 创建缓存…

文字游侠AI工具:一个高效内容创作的革命性助手,效率一键提高20倍!

在当今快节奏、高效率要求的信息时代&#xff0c;传统的内容生产方式已经难以满足不断增长的网络信息需求。随着人工智能技术的飞速发展&#xff0c;一系列创新的AI工具应运而生&#xff0c;极大地改变了我们处理信息和创造内容的方式。其中&#xff0c;文字游侠AI工具凭借其出…

校友会系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;校友管理&#xff0c;生活模块管理&#xff0c;兼职信息管理&#xff0c;表白墙管理&#xff0c;我的收藏管理&#xff0c;校友论坛&#xff0c;系统管理 微信端账号功能包括&…

css实现闪烁渐变背景,@property自定义属性

自 2024 年 7 月起&#xff0c;此功能适用于最新的设备和浏览器版本。此功能可能无法在较旧的设备或浏览器中使用。 property 是 CSS 中一个相对较新的功能&#xff0c;主要用于定义自定义属性&#xff08;即 CSS 变量&#xff09;的类型、继承性以及初始值。它允许开发者更好地…

DevEco Studio 预览器报错踩坑

This module is referencing one or more HSPs and cannot be previewed. To preview components in an HSP, switch to the HSP first. 不知道大家遇见这个问题没有 是因为我们在使用name跳转别的模块的页面时 引入了hsp模块进来 所以他在编译的时候 告诉我们这个模块中引…

pyro 教程 时间序列 单变量,重尾,python pytorch,教程和实例 Forecasting预测,布朗运动项、偏差项和协变量项

预测I:单变量&#xff0c;重尾 本教程介绍了预测模块&#xff0c;用Pyro模型进行预测的框架。本教程只涵盖单变量模型和简单的可能性。本教程假设读者已经熟悉慢病毒感染和张量形状. 另请参见: 预测II:状态空间模型 预测三:层次模型 摘要 要创建预测模型: 创建预测模型班级…

加密学中的零知识证明(Zero-Knowledge Proof, ZKP)到底是什么?

加密学中的零知识证明&#xff08;Zero-Knowledge Proof, ZKP&#xff09;到底是什么&#xff1f; 引言 在加密学的应用中&#xff0c;零知识证明&#xff08;Zero-Knowledge Proof, ZKP&#xff09;无疑是一颗璀璨的明星。它不仅挑战了我们对信息验证的传统认知&#xff0c;…

如何使用ssm实现理发店会员管理系统的设计和实现+vue

TOC ssm089理发店会员管理系统的设计和实现vue 绪论 1.1 选题背景 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。计算机软件可以针对不同行业的营业…

C语言刷题日记(附详解)(2)

一、有理数加法 输入格式&#xff1a; 输入在一行中按照a1/b1 a2/b2的格式给出两个分数形式的有理数&#xff0c;其中分子和分母全是整形范围内的正整数。 输出格式&#xff1a; 在一行中按照a/b的格式输出两个有理数的和。注意必须是该有理数的最简分数形式&#xff0c;若…

OpenCSG全网首发!Phi-3.5 Mini Instruct全参微调中文版

前沿科技速递&#x1f680; &#x1f389; 震撼发布&#xff01;OpenCSG正式推出全参数微调的Phi-3.5-mini-instruct中文版模型&#xff01; &#x1f50d; 本次发布的Phi-3.5-mini-instruct中文版模型基于最新的Phi-3.5架构&#xff0c;经过全参数微调&#xff0c;专为中文场景…

软件测试——JMeter安装配置

文章目录 JMeter介绍JMeter下载及配置配置错误 提示此时不应有...修改语言为中文 JMeter介绍 Apache JMeter 是 Apache 组织基于 Java 开发的压⼒测试⼯具&#xff0c;⽤于对软件做性能测试 JMeter下载及配置 环境要求&#xff1a;JDK版本在1.8及以上 下载压缩包&#xff0c;…

设计模式—代理模式

文章目录 以前自己做的笔记动态代理(重点)1.基于jdk的动态代理2.基于cglib的动态代理 新资料第 15 章 代理模式1、代理模式的基本介绍2、静态代码模式3、动态代理模式4、Cglib 代理模式5、代理模式(Proxy)的变体 代理模式是给某一个对象提供一个代理&#xff0c;并通过代理对象…

第12章 网络 (6)

12.8 网络层 12.8.4 分组转发 转发IP分组&#xff0c;根据目标地址分为&#xff1a; 1. 直接和本地相连。 2. 不直接相连&#xff0c;需要网关转发。 int ip_route_input_noref(skb, daddr, saddr, tos, net_dev)&#xff1a; //查找路由表。 如果 skb->_skb_r…

安捷伦色谱仪器LabVIEW软件替换与禁运配件开发

可行性分析及实现路径 可行性&#xff1a; 软件替换&#xff1a; 驱动程序支持&#xff1a; 要实现LabVIEW对安捷伦色谱仪器的控制&#xff0c;需要检查安捷伦是否提供LabVIEW驱动程序。如果没有现成的驱动&#xff0c;则可能需要开发自定义的驱动程序&#xff0c;通过LabVIEW…

微软推出全新多语言高质量Phi-3.5语言模型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

css flex布局 justify-content: space-between 最后两张居左

比如如果是8张&#xff0c;最后两张两边对齐&#xff0c;第八张最后一张 放个占位符就OK了 <div class"previewPadding flex" > <div class"picList picList3" v-for"(item,index) in picDataList" :key"index"> <…

6个免费字体网站,无需担心版权问题~

在设计项目中&#xff0c;选择合适的字体至关重要。然而&#xff0c;许多高质量的字体往往价格不菲。幸运的是&#xff0c;有一些网站提供了免费的商用字体&#xff0c;既能满足设计需求&#xff0c;又不需要额外的预算。在这篇文章中&#xff0c;分享6个免费商用字体网站&…

济南网站制作方案定制

在当今数字化时代&#xff0c;拥有一个专业的网站已经成为企业发展不可或缺的一部分。济南作为山东省的省会&#xff0c;经济发展迅速&#xff0c;各行各业对网站制作的需求也日益增加。因此&#xff0c;定制化的网站制作方案在济南显得尤为重要&#xff0c;能够帮助企业在激烈…

深入探究为什么 RAG 并不总是按预期工作:概述其背后的业务价值、数据和技术。

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 欢迎来到雲闪世界。我们将首先探讨决定基于 RAG 的项目成败的业务要素。然后&#xff0c;我们将深入探讨常见的技术障碍&#xff08;从数据处理到性能优化&#xff09;&#xff0c;并讨论克服这些障碍的策略…

数据结构(邓俊辉)学习笔记】优先级队列 10——左式堆:插入 + 删除

文章目录 1. 插入即是合并2. 删除亦是合并 1. 插入即是合并 以上&#xff0c;我们已经实现了&#xff0c;对于左式堆来说最为在意的合并算法。非常有意思的是&#xff0c;尽管合并操作并非优先级队列所要求的基本操作接口。但基于合并操作&#xff0c;我们却同样可以实现左式堆…