C++-线程池

1.1 线程池
        在处理大量并发任务的时候,如果按照传统的方式,来一个任务请求,创建一个线程来进行任务的处理,大量线程的创建和销毁,将消耗过多的系统资源,还增加了线程上下文(运行环境)切换的开销,而通过线程池就可以很好地解决这些问题。

        线程池技术通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程去处理任务,线程在处理完任务之后并不会销毁,而是把线程归还到线程池中,继续为后续的任务提供服务。

1.2 线程池的特点

1、控制并发性:对于多核处理器,由于多线程被分配到多个处理器中,提高并行处理的效率。线程池可以控制并发执行的线程数量,通过设定线程池的大小来限制系统中同时执行的线程数量,避免资源的过度占用和线程竞争导致的性能下降。

2、提高任务灵活性:线程池可以接受不同类型的任务,并对这些任务进行执行调度。它提供了一系列提交任务的方法,同时还支持定时执行和周期性执行任务的能力。

3、任务排队:当线程池中的线程已经全部被占用时,新提交的任务会被放入一个任务队列中进行排队等待执行。排队机制可以根据具体线程池的实现,选择不同的队列类型,如有界队列或无界队列。

4、提供线程管理和监控:线程池提供了易于管理和监控线程的方式,可以设置线程的属性、监控线程的状态、统计任务执行情况等。

5、线程复用:线程池会在内部维护一定数量的线程,并在需要时重复使用这些线程来执行任务,避免频繁地创建和销毁线程,从而提高性能和效率。

6、提高任务的响应速度,当任务到达时,不需要等待线程创建就能立即执行任务。

7、避免资源的过度消耗:线程池可以通过限制线程数量和排队机制来避免过度消耗资源。当系统负载过高时,线程池可以根据配置策略进行线程数量的自动调整,以保持系统的稳定性和性能。

1.3 按应用场景分类
1、FixedThreadPool:

固定线程池:线程池中的线程数量固定,这些线程会一直存在,不会随任务的增加或减少而动态调整,超出的任务会在队列中等待。

使用场景:任务量比较固定但耗时较长的任务。

2、CachedThreadPool:

缓存线程池:可根据需要创建新线程的线程池,如果新任务到达,但线程池中没有可用的线程,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。线程池中超过60s未使用的线程,将会被移除和销毁。

使用场景:任务量大但耗时少的任务。

3、WorkStealingPool:

工作窃取线程池:创建一个拥有多个任务队列(以便减少连接数)的线程池;

使用场景:会创建一个含有足够多线程的线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的CPU不会闲置,总会有活着的线程让CPU去运行。如果当前工作线程处理完自己本地任务队列中的任务时,就会去全局队列或其他工作线程的队列里面查找工作任务,帮助它们完成。利用Work Stealing,可以更好实现负载均衡。

4、ScheduledThreadPool:

计划线程池(定时线程池、调度线程池)

使用场景:定时以及周期性执行任务。

1.4 线程池模式
我将采用半同步/半异步模式又称为生产者消费者模式。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从同步队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适用于大数据量交换的场合。

1.5 线程池实现的关键技术分析
        线程池由三层组成:同步服务层、排队层和异步服务层,其中排队层居于核心地位,因为上层会将任务添加到排队层中,异步服务层同时也会取出任务,这里有一个同步的过程。在实现时,排队层就是一个同步队列,允许多个线程同时去添加或取出任务,并且要保证操作过程是安全的。

        线程池有两个活动过程,一个是往同步队列中添加任务的过程,另一个是从同步队列中取出任务的过程。

         从活动图中可以看到线程池的活动过程,一开始线程池会启动一定数量的线程,这些线程属于异步层,主要用来并行处理排队层中的任务,如果排队层中的任务数为空,则这些线程等待任务的到来,如果发现排队层中有任务了,线程池则会从等待的这些线程中唤醒一个来处理新任务。

        同步服务层则会不断地将新的任务添加到同步排队中,有可能上层的任务非常多,而任务又是非常耗时的,这时,异步层中的线程处理不过来,则同步排队层中的任务会不断增加,如果同步排队层不加上限控制,则可能会导致排队层中的任务过多,内存暴涨的问题。因此,排队层需要加上限的控制,当排队层中的任务数达到上限时,就不让上层的任务添加进来,起到限制和保护的作用。

2.线程池与进程池

2.1  线程池有计算密集型线程池和I/O密集型线程池,计算密集型线程池最大线程数:n+2(n为电脑逻辑处理器数量),I/O密集型线程池最大线程数:2n+2

2.2  线程池优点

2.2.1 开销较小

1.创建和销毁成本低 :线程的创建和销毁比进程相对轻量级。创建线程时,操作系统不需要像创建进程那样分配独立的地址空间等资源,线程共享进程的地址空间,因此线程的创建和切换速度通常比进程快。在需要频繁创建和销毁执行单元的场景下,线程池的性能开销相对较小。

2.资源占用少 :线程共享进程的资源,如内存、文件句柄等。所以在资源有限的情况下,使用线程池可以更高效地利用系统资源。例如,在一个内存有限的服务器上,如果使用进程池,每个进程都可能占用较大的内存空间,而线程池可以避免这种情况,使得更多的执行单元可以同时运行。

2.2.2 通信方便

线程之间可以直接访问共享内存中的数据,通信简单高效。不需要像进程之间通信那样通过复杂的 IPC(进程间通信)机制,如管道、消息队列、共享内存等。例如,在一个多线程的 Web 服务器中,线程可以直接操作共享的缓存数据结构,快速获取和更新数据,提高服务性能。

2.2.3  适合 I/O 密集型任务

对于 I/O 密集型任务,如处理大量网络请求、文件读写操作等,线程池可以很好地发挥作用。当线程执行 I/O 操作时,它会阻塞,但操作系统可以调度其他线程来执行任务。由于线程池中的线程已经存在,不需要为每个 I/O 任务都创建和销毁线程,这可以提高系统的响应速度和吞吐量。

2.3 进程池的优点

2.3.1 隔离性好

1. 数据隔离 :每个进程都有独立的内存空间,一个进程中的操作不会直接影响其他进程的数据。在多进程环境下,进程之间的数据隔离可以防止数据竞争和内存损坏等问题。例如,在一个视频处理系统中,如果一个进程在处理视频时出现错误,如内存溢出,它不会直接破坏其他进程的数据,从而提高系统的稳定性。

2. 错误隔离 :进程的独立性使得一个进程的崩溃不会直接影响其他进程。这对于一些关键任务的系统非常重要。例如,在一个分布式计算框架中,如果一个计算节点的进程出现故障,其他进程可以继续运行,并且可以重新启动故障进程来恢复任务。

2.3.2  利用多核 CPU

2.3.3  安全性高

进程之间的隔离性也提高了系统的安全性。因为一个进程无法直接访问其他进程的内存空间,这可以防止恶意代码在不同进程之间传播。例如,在一个操作系统中,用户进程和系统进程之间通过进程隔离机制来保证系统的安全,防止用户进程直接篡改系统关键数据。

2.4  为什么有时候更倾向于线程池开发?

1. 性能要求高且任务简单 :如果应用程序需要处理大量的并发任务,且这些任务相对简单、执行时间较短,并且对数据共享和通信要求较高,线程池是更好的选择。例如,一个高性能的 Web 服务器需要同时处理成千上万的客户端请求,每个请求的处理主要是读取和写入网络数据,以及简单的数据处理操作。使用线程池可以快速创建和调度线程来处理这些请求,同时由于线程之间的通信方便,可以高效地共享一些缓存数据,如路由信息、会话数据等。

2. 适合 I/O 密集型场景 :在以 I/O 操作为主的场景下,如文件服务器、代理服务器等,线程池的优势更加明显。因为线程在等待 I/O 操作完成期间可以释放 CPU,让其他线程使用 CPU,从而提高系统的整体性能。而进程池在这种情况下,由于进程之间的切换和通信相对复杂和耗时,可能不如线程池高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值