并发编程与多线程

并发编程

进程:

计算机上正在运行的某一个应用程序,是资源(内存,CPU等)分配的基本单位,当在计算机上运行某个应用程序时,操作系统会给当前的应用程序,分配资源,并且创建对应的进程,将进程放入进程队列中,进程调度器会为当前进程分配对应的CPU时间,运行程序。
通信方式:管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams

线程:

线程是一条执行线路,是程序执行的最小单位,是进程的一个执行流,是CPU调度和分流的最基本的单位,一个进程可以有多个线程,线程之间共享进程的所有资源,每个线程有自己的内存空间,线程由CPU调度执行,允许多个线程同时运行。
通信方式:
1、 共享内存
多个线程共享同一块内存区域,通过读写共享内存来实现信息交流和数据共享。需要考虑线程安全问题,可以使用互斥锁、信号量等机制来保证数据的一致性。

2、信号量
通过信号量来实现线程之间的同步和互斥。通过P操作和V操作来改变信号量的值,当信号量的值为0时,线程需要等待;当信号量的值大于0时,线程可以继续执行。

3、互斥锁
通过互斥锁来实现线程之间的互斥访问共享资源。当一个线程获取到互斥锁时,其他线程需要等待;当一个线程释放互斥锁时,其他线程可以竞争获取锁。

4、条件变量
通过条件变量来实现线程之间的等待和唤醒。当线程需要等待某个条件满足时,可以调用条件变量的等待函数使自己进入等待状态;当条件满足时,可以调用条件变量的唤醒函数唤醒等待的线程。

5、管道
通过管道来实现线程之间的通信。一个线程可以将数据写入管道,另一个线程可以从管道中读取数据。需要注意管道的大小限制和线程安全问题。

协程:
并发:

当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行
时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起
状。这种方式我们称之为并发(Concurrent)。

高并发

高并发(High Concurrency)是现在互联网设计系统中需要考虑的一个重要因素之一,通常来说,就是通过严谨
的设计来保证系统能够同时并行处理很多的请求。这就是大家常说的「 高并发 」。
高并发是指多个执行单元同时、并行被执行,而并发的执行单位对于共享资源(硬件资源和软件上的全局变量、静
态变量等)的访问很容易导致竞态(race conditions)

并行:

当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线
程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

进程与线程的区别:

线程是进程的基本执行单元,‌一个进程的所有任务都在线程中执行。‌进程要想执行任务,‌必须得有线程。‌一个进程可以包含多个线程,‌这些线程共享相同的资源。‌进程和线程都是为了实现程序的并发执行,‌提高系统资源的利用率。
定义:‌
进程是程序的一次执行过程,‌拥有独立的内存空间、‌系统资源和状态。‌线程是进程内的一个独立执行单元,‌共享相同的内存空间和系统资源。‌
资源开销:‌
创建和销毁进程的开销较大,‌因为进程拥有独立的地址空间和资源。‌而线程的创建和销毁开销较小,‌因为它们属于同一个进程,‌共享相同的地址空间。‌
通信与同步:‌
进程间通信需要复杂的机制,‌如管道、‌消息队列、‌共享内存等。‌线程之间可以通过共享内存等简单的机制进行通信,‌但需要考虑同步问题,‌以避免竞态条件和死锁。‌
独立性:‌
进程是相互独立的执行单元,‌一个进程的崩溃不会影响其他进程。‌线程是进程内的执行单元,‌共享相同的资源,‌一个线程的错误可能影响整个进程。‌
系统开销:‌
进程切换的开销较大,‌涉及到保存和恢复整个进程的状态。‌线程切换的开销相对较小,‌因为线程共享相同的地址空间,‌只需保存和恢复线程私有的状态。‌

进程和线程在操作系统中扮演着不同的角色,‌进程提供较强的隔离性和资源拥有权,‌而线程提供轻量级的并发执行和资源共享。‌

协程与线程的区别:

1、调度方式
协程由编程者控制,协程之间可以有优先级;
线程由系统控制,一般没有优先级。线程是CPU最小的调度单位。
2、调度速度
协程几乎比线程快一个数量级。协程调用由编码者控制,可以减少无效的调度
3、资源占用
协程可以控制内存占用量,灵活性更好;线程是由系统控制
4、创建数量
协程的使用更灵活(有优先级控制、资源使用可控),调度速度更快,相比与线程而言,调度损耗更小,所以真实可创建且有效的协程数量可以比线程多很多。同样,因为调度和资源的限制,有效协程的数量也是有上限的。

多线程

线程状态切换

创建状态:当一个线程被 new 创建的时候
就绪状态:当线程被 start() 调用的时候,该线程就随时可以获得 cpu 的调度
运行状态:当一个线程获得 cpu 的资源,被 cpu 调度执行的时候
阻塞状态:当线程被 sleep ()或者 wait 进行休眠的时候,就是阻塞状态,但休眠结束后,会自动变成就绪状态,或者被 notify 唤醒也会变成就绪状态
终止状态 : 线程结束,即终止状态

线程同步:

同步:在某一个时刻只有一个线程在执行。
原则 :
1 、原子性:一系列操作作为整体执行,要么执行,要么都不执行。
2 、可见性 : 线程在操作数据的过程中,可以被别的线程发现
3 、有序性 : 执行顺序不会被编译器进行指令重排序

死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可
能正常终止。

线程安全性:

竞态条件(Race Condition):

竞态条件是指当多个线程同时访问共享资源,并且至少有一个线程同时对资源进行写操作时,导致结果依赖于线程执行的顺序或时间。
竞态条件会导致不确定的结果,甚至可能破坏数据的完整性。

线程安全性的等级:

不可重入性(Non-Reentrant):函数不能被多个线程同时调用,需要外部同步来保证安全。
部分线程安全(Partially Thread-Safe):函数可以同时被多个线程调用,但要求在访问共享资源时进行额外的同步。
线程安全(Thread-Safe):函数在多个线程中调用时不需要额外的同步措施,可以正确地并发执行。
无等级(Non-Levelled):函数在某些情况下是线程安全的,但在其他情况下不是。

线程安全的实现方式:

互斥锁(Mutex):通过对共享资源加锁,保证同一时间只有一个线程可以访问或修改共享资源。
条件变量(Condition Variable):用于线程间的条件等待和通知,使得线程可以在特定条件满足时进行同步。
原子操作(Atomic Operations):保证对共享变量的操作是不可中断的,确保线程间的数据一致性。
同步原语和算法:如信号量、屏障、读写锁等,用于实现更复杂的线程同步需求。

C++标准库中的线程安全性保证:

标准库中的某些容器和算法函数具有特定的线程安全性保证,比如std::vector的读操作是线程安全的,而写操作则需要外部同步保护。
标准库提供的一些原子操作和并发容器也具有相应的线程安全性保证,可以在多线程环境下安全地进行操作。

开发者自定义线程安全性:

开发者可以通过使用互斥锁、条件变量和原子操作等机制,来保证自己编写的代码在多线程环境中的线程安全性。
在设计和实现多线程程序时,需要仔细考虑和分析线程安全性问题,并采取适当的同步机制来确保安全性。

如何避免线程死锁

破坏互斥条件
这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
破坏请求与保持条件
一次性申请所有的资源。
破坏不剥夺条件
占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
破坏循环等待条件
靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

线程池

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。
任务调度以执行线程的常见方法是使用同步队列,称作任务队列。池中的线程等待队列中的任务,并把执行完的任务放入完成队列中。

线程池模式一般分为两种:HS/HA半同步/半异步模式、L/F领导者与跟随者模式。

半同步/半异步模式又称为生产者消费者模式:

是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适于大数据量交换的场合。

领导者跟随者模式

在线程池中的线程可处在3种状态之一:领导者leader、追随者follower或工作者processor。任何时刻线程池只有一个领导者线程。事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。这一模式实现复杂,但避免了线程间交换任务数据,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现。

线程池的伸缩性对性能有较大的影响。

创建太多线程,将会浪费一定的资源,有些线程未被充分使用。
销毁太多线程,将导致之后浪费时间再次创建它们。
创建线程太慢,将会导致长时间的等待,性能变差。
销毁线程太慢,导致其它线程资源饥饿。

线程池的关键组成部分

线程池管理器(ThreadPoolExecutor):负责创建、管理和控制线程池。它负责线程的创建、销毁和管理,以及线程池的状态监控和调度任务。
工作队列(BlockingQueue):用于存储待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入工作队列中等待执行。
线程池线程(Worker Thread):实际执行任务的线程。线程池中会维护一组线程,这些线程可以被重复使用,从而避免了频繁创建和销毁线程的开销。

线程池的运行机制如下:

当任务到达时,线程池管理器会检查线程池中是否有空闲的线程。如果有,则将任务分配给空闲线程执行;如果没有,则进入下一步。
如果线程池中的线程数量未达到最大限制,线程池管理器会创建一个新的线程,并将任务分配给该线程执行。
如果线程池中的线程数量已达到最大限制,并且工作队列未满,则将任务放入工作队列中等待执行。
当线程池中的线程执行完任务后,会从工作队列中获取下一个任务并执行。
线程池的优点包括重用线程、控制并发度、提供线程管理和监控等。通过适当地配置线程池的大小和任务队列的容量,可以充分利用系统资源,提高程序的性能和响应速度。同时,线程池可以避免

线程池的七大参数:

1.核心线程数量;>0;
2.最大线程数量;
3.空闲线程的最大存活时间;
4.时间单位;
5.任务队列;
6.线程工厂;
7.拒绝策略;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值