进程与线程
简介
代码 === 程序
进程: 一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。
线程: 调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程,进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
特性
场景
- 多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。
- 多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)。
- 协程可以处理 IO 密集型程序的效率,但是处理 CPU 密集型不是它的长处,如要充分发挥 CPU 利用率可以结合多进程 + 协程。
差异
- 进程是并行:同一时刻多个任务同时在运行。
- 线程是并发:同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。
协程
- 执行效率极高,因为子程序切换(函数)不是线程切换,由程序自身控制,没有切换线程的开销。所以与多线程相比,线程的数量越多,协程性能的优势越明显。
- 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共 享资源时也不需要加锁,因此执行效率高很多。
多线程竞争
同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态。
数据几乎同步会被多个线程占用,造成数据混乱 ,即所谓的线程不安全。
锁
锁的好处:确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。
锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大的下降了
锁的致命问题:死锁。
死锁:若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。
区别
1是多进程 VS 2是多线程
数据共享、同步:
- 数据共享复杂,需要用IPC;数据是分开的,同步简单
- 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂
优劣:各有优势
内存、CPU:
- 占用内存多,切换复杂,CPU利用率低
- 占用内存少,切换简单,CPU利用率高
优劣:线程占优
创建销毁、切换:
- 创建销毁、切换复杂,速度慢
- 创建销毁、切换简单,速度很快
优劣:线程占优
编程、调试:
- 编程简单,调试简单
- 编程复杂,调试复杂
优劣:进程占优
可靠性:
- 进程间不会互相影响
- 一个线程挂掉将导致整个进程挂掉
优劣:进程占优
分布式:
- 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单
- 适应于多核分布式
优劣:进程占优
高并发
高并发是指系统在同一时间段内同时处理大量并发请求的能力。在计算机领域,尤其是在网络应用程序中,高并发是一个重要的性能指标和挑战。
并发与并行:并发(
Concurrency
)是指系统能够同时处理多个任务或请求,而不一定是同时执行;而并行(Parallelism
)是指系统能够同时执行多个任务或请求。高并发通常涉及到并发处理的能力,即同时处理大量请求。高并发的挑战:在面对高并发的情况下,系统可能面临以下挑战:
- 系统负载过高:大量并发请求会给系统带来巨大的负载压力,可能导致系统资源耗尽、响应变慢甚至崩溃。
- 锁竞争和资源冲突:多个请求同时对共享资源进行读写操作时,可能导致锁竞争和数据一致性问题。
- 数据库瓶颈:数据库通常是高并发系统的瓶颈之一,需要合理设计数据库架构以支持高并发读写操作。
解决高并发的方案:
- 水平扩展:通过增加服务器数量,将负载分布到多台服务器上,以提高系统的并发处理能力。
- 缓存:使用缓存来减轻对后端资源(如数据库)的压力,提高读取数据的速度。
- 异步处理:采用异步处理方式,将一些耗时的操作(如IO操作)放到后台线程或任务队列中处理,保持主线程的高并发能力。
- 负载均衡:使用负载均衡技术将请求分发到多个服务器上,避免单个服务器过载。
- 高效算法和数据结构:选择合适的算法和数据结构以提高系统的处理效率和响应速度。
- 垂直拆分和微服务架构:将系统按照功能模块进行拆分,通过微服务架构实现不同模块的独立部署和扩展。
测试和监控:高并发系统需要进行充分的测试和监控,以确保系统在高负载下的稳定性和性能。常用的测试方法包括压力测试、性能测试和容量规划等。
TIP
总的来说,解决高并发问题需要综合考虑系统架构设计、服务器扩展能力、缓存策略、异步处理、负载均衡和合理使用算法等因素。通过合理的架构设计和优化方法,可以提高系统的高并发处理能力,提供更好的用户体验和稳定性。
轻量级与大规模
轻量级的高并发和大规模的高并发是针对应用程序负载的不同类型和规模进行的一种分类。
轻量级的高并发:通常是指一个系统每秒钟处理几千到几万个请求的情景,如小型电商平台或企业内部管理系统等。相对于大规模高并发而言,轻量级高并发要求的系统性能较低,通常不需要应对如海量数据、复杂业务逻辑等问题。
大规模的高并发:通常是指一个系统每秒钟要处理百万甚至亿级别的请求,如互联网搜索引擎、社交网络、电商平台等。这种情况下,系统需要具有非常高的处理能力和可靠性,以保证服务质量和用户体验。相对于轻量级高并发而言,大规模高并发要求的系统性能更高,并且需要应对更多的复杂问题,如分布式架构、缓存、负载均衡、高可用性等。
解决轻量级高并发和大规模高并发的方案也存在差异:
- 轻量级高并发:可以采用简单的单机服务器或者集群,使用一些常见的技术解决方案如连接池、线程池、缓存等来提高数据处理效率和响应速度。
- 大规模高并发:需要借助分布式架构、负载均衡、缓存和异步处理等技术来实现高效可靠的系统。例如通过分布式数据库或者
NoSQL
数据库来存储和管理数据,通过分布式消息队列来解耦各个模块之间的依赖,使得系统能够横向扩展。
TIP
总结起来,轻量级高并发要求的系统性能较低,相对简单,主要需要一些常见的处理方案,而大规模高并发则需要更加精细的设计和实现,采用分布式架构和相关的高并发处理技术。不过两者面临的挑战都十分重要,需要合理的架构设计、性能测试和监控手段来保证系统的稳定性和高效性。
高并发语言选型
在高并发方面,没有绝对的胜负之分,因为选择最适合的编程语言取决于具体的应用场景和需求。以下是 Node
和 Go
在高并发方面的一些比较:
Node.js 优势:
- 事件驱动和非阻塞 I/O 模型,适用于处理大量并发请求。
- 单线程运行,可以有效地利用系统资源。
- 异步编程模型,可以处理复杂的异步操作,提升性能。
Go 语言优势:
- 原生支持多线程,并通过协程实现轻量级的线程切换调度,适用于处理 I/O 密集型和 CPU 密集型任务。
- 静态类型和强类型检查,在编译时能够捕获错误,提高代码的健壮性和可靠性。
- 内置网络库和丰富的支持库,方便构建高效的网络服务。
TIP
对于大规模的高并发应用,Go 语言的多线程特性和静态类型检查可能会使其在一些方面更胜一筹。而对于轻量级的高并发应用,Node.js 的事件驱动、非阻塞 I/O 和异步编程模型可能更加适合。
TIP
在选择编程语言时,还需要考虑团队技能、开发效率、生态系统、可维护性等因素。最好的选择是根据具体需求和团队的实际情况进行评估,并进行性能测试和调优,以选择最合适的工具和框架来处理高并发应用程序。