Linux下CC++ 手写一个线程池-

Linux下CC++ 手写一个线程池-,第1张

在我们日常生活中会遇到许许多多的问题,如果一个服务端要接受很多客户端的数据,该怎么办?多线程并发内存不够怎么办?所以我们需要了解线程池的相关知识。

1线程池的简介

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

2线程池的组成

1、线程池管理器(ThreadPoolManager):用于创建并管理线程池

2、工作线程(WorkThread): 线程池中线程

3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。

4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。

3线程池的主要优点

1避免线程太多,使得内存耗尽

2避免创建与销毁线程的代价

3任务与执行分离

1线程池结构体定义

代码如下(示例):

相关视频推荐

150行代码,带你手写线程池,自行准备linux环境

C++后台开发该学哪些内容,标准技术路线及面经与算法该如何刷

学习地址:C/C++Linux服务器开发/后台架构师零声教育-学习视频教程-腾讯课堂

需要更多C/C++ Linux服务器架构师学习资料加qun 812855908 (资料包括C/C++,Linux,golang技术,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg,大厂面试题 等)

2接口定义

代码如下(示例):

3回调函数

代码如下(示例):

4全部代码(加注释)

代码如下(示例):

关于线程池是基本代码就在上面了,关于编程这一部分内容,我建议大家还是要自己去动手实现,如果只是单纯的看了一遍,知识这块可能会记住,但是 *** 作起来可能就比较吃力,万事开头难,只要坚持下去,总会有结果的。

当前网络技术创新中面临的最核心问题是网络资源管理机制问题,承载网络需要通过构建统一健壮的网络资源管理层,实现对网络资源的统一架构管理与高效灵活的按需调度,真正实现“网随云动”。接下来我为大家推荐的是网络资源管理的重要性,欢迎阅读。

一、网络资源管理是网络技术发展的核心问题

当前网络资源管理机制面临严重挑战,网络资源管理理念和方式将迎来重大技术变革。

首先,粗放的网络资源管理方式面临巨大成本建设成本压力。自从运营商的主营业务从语音逐步转向以视频为代表的数据业务以来,运营商一直面临网络建设成本远高于网络运营收入的量收 “剪刀差”的困扰。运营商量收剪刀差由2012年的18个百分点持续拉大至2015年的209个百分点,通信业增量不增收的趋势进一步显现。当前IP网络一直坚持“弱管理”的理念,分布式路由导致全局网络带宽利用率不足30%;“Best Effort”机制通过建立多张网络并采用轻载方式用带宽成本换取服务质量,导致各网络之间资源不能共享,网络成本增加,网络效率低下。吉尔德定律决定主干网带宽大概是每八个月增长一倍,这种粗放的网络资源管理方式的建设和运维成本将指数级增加。

其次,刚性的网络资源管理方式难以支持个性化业务。云计算的发展使得网络互联对象变成了“虚拟机(VM),VM的灵活部署对网络资源调配提出了d性化要求。但是,现有网络技术难以高效支持大规模的跨域虚拟机互联,成为制约云计算发展的重要因素。光传送网通常按照业务最大峰值配置静态带宽,造成业务费用高昂,网络资源浪费,难以适应云互联需求。IP网络层面VxLAN技术是普遍采用的VM互联技术,但是VxLAN只是实现了虚拟专用“线”,在组大网时,面临STP收敛缓慢,以及组网的N平方问题,很难作为大范围使用的VM跨域组网技术。

另外,孤立的网络资源管理方式难以满足融合业务需求。目前IP网络与CDN网络缺乏必要的协同调度机制,运营商的IP网络和WDM网络各自建设、独立规划、分配、管理和控制,造成网络灵活性弱,资源效率低。IP层把光层看成静态硬管道,在网络流量不均衡、IP网络拥塞的情况下,无法利用光层资源及时进行流量疏导和带宽提升。由于IP网络看不到光层网络拓扑和保护能力,导致光层和IP层均配置了保护,一个业务需要4倍带宽,网络资源效率低下。

二、统一强健的网络资源管理机制是网络技术创新重要方向

统一强健的网络资源管理机制具体体现在以下几个方面:一是粗放式管理向精细化管理转变:由基于优先级的QoS管理深入到基于队列的网络管理。从虚拟专用“线”向虚拟专用“网”的转变。二是刚性方式向d性方式转变:高效支持大规模的跨域虚拟机互联,实现每个虚拟网络中的网络资源的灵活部署,按需调度。三是孤立资源向融合统一调度转变:对IP网络、光网络资源,异构网络设备,以及计算、存储、网络资源的“池化”和统一管理与调度。

要构建跨网络、跨域的'统一架构的灵活的网络资源管理层,实现网络虚拟化,需要具备以下三个方面的基本要素。

(1)开放网络架构模型

“开放”是产业链成熟的良性发展的重要标志,开放可以技术创新活力,降低创新成本,优化产业链条。类比于传统机械设备的开放,正是由于零件的标准化和互换性,促进了工业革命的深入发展,真正带来了工业化大浪潮。然而,传统的网络设备大多是封闭的架构,各厂商独有设计开发,具有固化、封闭式、开发周期长的特点,产业生态相对封闭,创新能力不强。而SDN/NFV技术的出现,开启了网络开放的浪潮,特别是NFV技术将推动网络功能接口的标准化和功能重组。总体而言,现阶段网络开放已成为技术和产业发展的必然趋势。

(2) 网络 *** 作系统

在软件定义网络的体系架构中,网络 *** 作系统在控制平面扮演者重要的角色,它通过南向接口实现对网元资源的抽象和管理,北向接口向上层应用提供网络服务,同时实现业务编排的功能,根据不同策略来编排用户数据的转发路径。网络 *** 作系统本质上要为用户选择一条满足用户QoS等需求的从源到目的转发路径。因此,它需要具有拓扑信息收集,路径计算等基本功能。网络 *** 作系统从一出现就得到了ICT厂商的极大关注。在开源社区的大力推动下,如今网络 *** 作系统的产业生态呈现一片欣欣向荣的发展态势。到2015年中期,网络 *** 作系统开始产业聚焦,基本形成了由Linux基金会主持管理的Opendaylight解决方案和以ONLab主持管理的ONOS解决方案两大技术方向,全球知名ICT厂商、电信运营商以及学术研究机构纷纷加入其中,贡献自己的力量。

(3)数据面新型转发技术

当前,在数据转发平面有两个关键技术已成为业界关注的热点。一个是利用Intel DPDK开发套件提高软转发的性能;另一个是协议无关的转发,这将成为未来网络虚拟化和网络可定制的基础。

将软件和硬件解耦,采用通用硬件实现软转发的模式,在性能上将存在巨大挑战。当前,Intel开源了一套数据面开发接口套件DPDK(Data Plane Develop Kit),它是一套基于Intel架构的通用处理器的数据包处理程序开发工具和平台,可以帮助用户更便捷的开展数据面的创新,将控制面和数据面平台进行高效整合。采用DPDK,在性能方面,单个Intel至强处理器可以达到超过80Mpps的吞吐量,在多核配置中则可以更高,超过10Gbps线速。目前,基于DPDK的开源生态正在积极构建,包括Intel、6WIND、WindRiver、Aricent等公司都是主导机构。在物理网卡方面,也有包括Intel、Mellanox、Broadcom、Cisco等公司也都在积极研发下一代的物理网卡。

协议无关转发(POF,Protocol Oblivious Forwarding)是SDN数据平面灵活可编程的创新技术,其通过对网络的转发行为进行抽象,实现利用高级编程语言对多种转发平台进行灵活的转发协议和转发流程定制。当前,协议无关的转发已成为业界研究的热点,ONF标准组正在积极定义相关标准,硅谷的创新公司和华为、中兴等公司都在积极推进PoF芯片的研发,推进产业生态的建设。

三、我国网络技术创新和升级演进目标与方向

当前,我国应该加快转变传统粗放式的网络资源管理理念和运营模式,以构建开放架构的网络资源管理平台为核心,打造高效d性的网络资源池化管理和按需调度的能力,支持极度差异化的网络定制服务。在网络技术创新方向,网络 *** 作系统和协议无关转发是技术主攻方向。重点聚焦于:

(1)研发网络虚拟化技术与平台,突破基于可控队列的流表隔离及虚拟技术、基于无连接方式的跨域虚拟专网技术。

(2)研发网络功能定制与业务编排技术与系统,攻克网络与IT资源的联合编排技术、基于模型的网络功能链定义技术。

(3)研发支持队列级资源切片的NP芯片与设备。

在网络升级演进方向,实现从“智能管道”向“开放融合网络资源平台”的根本转变:

(1)构建开放架构的网络资源管理平台。一是实现IP网络资源的整合,基于集群构建网络资源池——“资源云”;二是实现IP与光资源的统一架构管理,构建强健的网络资源管理层。

(2)建设网络资源的开放与定制环境。实现网络资源的有序开放,支持网络虚拟运营商的特定功能的d性定制。

拓展内容:

《网络资源与网络资源管理》致力于全面阐述网络资源及其管理理论,将努力实现:结合网络、通信技术,运用经济学、管理学基本工具,在现有的网络管理、信息资源管理理论研究的基础上,建立一个网络资源管理的解释框架,帮助后来的学习者更加准确地认识和把握网络资源管理中的一些概念和词汇,洞察网络资源管理的理论组成和本质所在,为实现网络资源管理的目标奠定理论基础。

锁出现的原因

临界资源是什么: 多线程执行流所共享的资源

锁的作用是什么, 可以做原子 *** 作, 在多线程中针对临界资源的互斥访问 保证一个时刻只有一个线程可以持有锁对于临界资源做修改 *** 作

任何一个线程如果需要修改,向临界资源做写入 *** 作都必须持有锁,没有持有锁就不能对于临界资源做写入 *** 作

锁 : 保证同一时刻只能有一个线程对于临界资源做写入 *** 作 (锁地功能)

再一个直观地代码引出问题,再从指令集的角度去看问题

上述一个及其奇怪的结果,这个结果每一次运行都可能是不一样的,Why ? 按照我们本来的想法是每一个线程 + 20000000 结果肯定应该是60000000呀,可以就是达不到这个值

为何? (深入汇编指令来看) 一定将过程放置到汇编指令上去看就可以理解这个过程了

a++; 或者 a += 1; 这些 *** 作的汇编 *** 作是几个步骤

其实是三个步骤:

正常情况下,数据少, *** 作的线程少,问题倒是不大,想一想要是这样的情况下, *** 作次数大,对齐 *** 作的线程多,有些线程从中间切入进来了,在运算之后还没写回内存就另外一个线程切入进来同时对于之前的数据进行++ 再写回内存, 啥效果,多次++ *** 作之后结果确实一次加加 *** 作后的结果。 这样的 *** 作 (术语叫做函数的重入) 我觉得其实就是重入到了汇编指令中间了,还没将上一次运算的结果写回内存就重新对这个内存读取再运算写入,结果肯定和正常的逻辑后的结果不一样呀

来一幅解释一下

咋办 其实问题很清楚,我们只需要处理的是多条汇编指令不能让它中间被插入其他的线程运算 (要想自己在执行汇编指令的时候别人不插入进来) 将多条汇编指令绑定成为一条指令不就OK了嘛。

也就是原子 *** 作!!!

不会原子 *** 作? *** 作系统给咱提供了线程的 绑定方式工具呀:mutex 互斥锁(互斥量), 自旋锁(spinlock), 读写锁(readers-writer lock) 他们也称作悲观锁 作用都是一个样,将多个汇编指令锁成为一条原子 *** 作 (此处的汇编指令也相当于如下的临界资源)

悲观锁:锁如其名,每次都悲观地认为其他线程也会来修改数据,进行写入 *** 作,所以会在取数据前先加锁保护,当其他线程想要访问数据时,被阻塞挂起

乐观锁:每次取数据的时候,总是乐观地认为数据不会被其他线程修改,因此不上锁。但是在更新数据前, 会判断其他数据在更新前有没有对数据进行修改。

互斥锁

最为常见使用地锁就是互斥锁, 也称互斥量 mutex

特征,当其他线程持有互斥锁对临界资源做写入 *** 作地时候,当前线程只能挂起等待,让出CPU,存在线程间切换工作

解释一下存在线程间切换工作 : 当线程试图去获取锁对临界资源做写入 *** 作时候,如果锁被别的线程正在持有,该线程会保存上下文直接挂起,让出CPU,等到锁被释放出来再进行线程间切换,从新持有CPU执行写入 *** 作

互斥锁需要进行线程间切换,相比自旋锁而言性能会差上许多,因为自旋锁不会让出CPU, 也就不需要进行线程间切换的步骤,具体原理下一点详述

加互斥量(互斥锁)确实可以达到要求,但是会发现运行时间非常的长,因为线程间不断地切换也需要时间, 线程间切换的代价比较大

相关视频推荐

你绕不开的组件—锁,4个方面手撕锁的多种实现

“惊群”原理、锁的设计方案及绕不开的“死锁”问题

学习地址:C/C++Linux服务器开发/后台架构师零声教育-学习视频教程-腾讯课堂

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享

自旋锁

spinlock自旋锁

对比互斥量(互斥锁)而言,获取自旋锁不需要进行线程间切换,如果自旋锁正在被别的线程占用,该线程也不会放弃CPU进行挂起休眠,而是恰如其名的在哪里不断地循环地查看自旋锁保持者(持有者)是否将自旋锁资源释放出来 (自旋地原来就是如此)

口语解释自旋:持有自旋锁的线程不释放自旋锁,那也没有关系呀,我就在这里不断地一遍又一遍地查询自旋锁是否释放出来,一旦释放出来我立马就可以直接使用 (因为我并没有挂起等待,不需要像互斥锁还需要进行线程间切换,重新获取CPU,保存恢复上下文等等 *** 作)

哪正是因为上述这些特点,线程尝试获取自旋锁,获取不到不会采取休眠挂起地方式,而是原地自旋(一遍又一遍查询自旋锁是否可以获取)效率是远高于互斥锁了 那我们是不是所有情况都使用自旋锁就行了呢,互斥锁就可以放弃使用了吗

解释自旋锁地弊端:如果每一个线程都仅仅只是需要短时间获取这个锁,那我自旋占据CPU等待是没啥问题地。要是线程需要长时间地使用占据(锁)。。。 会造成过多地无端占据CPU资源,俗称站着茅坑不拉屎 但是要是仅仅是短时间地自旋,平衡CPU利用率 + 程序运行效率 (自旋锁确实是在有些时候更加合适)

自旋锁需要场景:内核可抢占或者SMP(多处理器)情况下才真正需求 (避免死锁陷入死循环,疯狂地自旋,比如递归获取自旋锁 你获取了还要获取,但是又没法释放)

自旋锁的使用函数其实和互斥锁几乎是一摸一样地,仅仅只是需要将所有的mutex换成spin即可

仅仅只是在init存在些许不同

何为惊群,池塘一堆, 我瞄准一条插过去,但是好似所有的都像是觉着自己正在被插一样的四处逃窜。 这个就是惊群的生活一点的理解

惊群现象其实一点也不少,比如说 accept pthread_cond_broadcast 还有多个线程共享epoll监视一个listenfd 然后此刻 listenfd 说来 SYN了,放在了SYN队列中,然后完成了三次握手放在了 accept队列中了, 现在问题是这个connect我应该交付给哪一个线程处理呢

多个epoll监视准备工作的线程 就是这群 (),然后connet就是鱼叉,这一叉下去肯定是所有的 epoll线程都会被惊醒 (多线程共享listenfd引发的epoll惊群)

同样如果将上述的多个线程换成多个进程共享监视 同一个 listenfd 就是(多进程的epoll惊群现象)

咱再画一个草图再来理解一下这个惊群:

如果是多进程道理是一样滴,仅仅只是将所有的线程换成进程就OK了

终是来到了今天的正题了: epoll惊群问题地解决上面了

首先 先说说accept的惊群问题,没想到吧accept 平时大家写它的多线程地时候,多个线程同时accept同一个listensock地时候也是会存在惊群问题地,但是accept地惊群问题已经被Linux内核处理了: 当有新的连接进入到accept队列的时候,内核唤醒且仅唤醒一个进程来处理

但是对于epoll的惊群问题,内核却没有直接进行处理。哪既然内核没有直接帮我们处理,我们应该如何针对这种现象做出一定的措施呢

惊群效应带来的弊端: 惊群现象会造成epoll的伪唤醒,本来epoll是阻塞挂起等待着地,这个时候因为挂起等待是不会占用CPU地。。。 但是一旦唤醒就会占用CPU去处理发生地IO事件, 但是其实是一个伪唤醒,这个就是对于线程或者进程的无效调度。然而进程或者线程地调取是需要花费代价地,需要上下文切换。需要进行进程(线程)间的不断切换 本来多核CPU是用来支持高并发地,但是现在却被用来无效地唤醒,对于多核CPU简直就是一种浪费 (浪费系统资源) 还会影响系统的性能

解决方式(一般是两种)

Nginx的解决方式:

加锁:惊群问题发生的前提是多个进程(线程)监听同一个套接字(listensock)上的事件,所以我们只让一个进程(线程)去处理监听套接字就可以了。

画两张图来理解一下:

上述还没有进行一个每一个进程都对应一个listensock 而是多线程共享一个listensock 运行结果如下

所有的线程同时被唤醒了,但是实际上会处理连接的仅仅只是一个线程,

咱仅仅只是将主线程做如上这样一个简单的修改,每一个线程对应一个listensock;每一个线程一个独有的监视窗口,将问题抛给内核去处理,让内核去负载均衡 : 结果如下

仅仅唤醒一个线程来进行处理连接,解决了惊群问题

本文通过介绍两种锁入手,以及为什么需要锁,锁本质就是为了保护,持有锁你就有权力有能力 *** 作写入一定的临界保护资源,没有锁你就不行需要等待,本质其实是将多条汇编指令绑定成原子 *** 作

然后介绍了惊群现象,通过一个巧妙地例子,扔一颗石子,只是瞄准一条鱼扔过去了,但是整池鱼都被惊醒了,

对应我们地实际问题就是, 多个线程或者进程共同监视同一个listensock。。。。然后IO连接事件到来地时候本来仅仅只是需要一个线程醒过来处理即可,但是却会使得所有地线程(进程)全部醒过来,造成不必要地进程线程间切换,多核CPU被浪费喔,系统资源被浪费

处理方式 一。 Nginx 源码加互斥锁处理。。 二。设置SO_REUSEPORT, 使得多个进程线程可以同时连接同一个port , 为每一个进程线程搞一个listensock 将问题抛给内核去处理,让他去负载均衡地仅仅将IO连接事件分配给一个进程或线程


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/10764217.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-11
下一篇 2023-05-11

发表评论

登录后才能评论

评论列表(0条)

保存